Install

Install

Peer dependency: astro >= 4. @love-rox/tcy-core and @love-rox/tcy-rehype are pulled in automatically.

bun

bun add @love-rox/tcy-astro

pnpm

pnpm add @love-rox/tcy-astro

npm

npm i @love-rox/tcy-astro

yarn

yarn add @love-rox/tcy-astro

Pieces

Two pieces

tcy() integration

Registers @love-rox/tcy-rehype on Astro's Markdown / MDX pipeline so half-width alphanumerics in your .md and .mdx content are wrapped automatically at build time.

<Tcy> component

An Astro component to wrap a slot inside .astro files explicitly — useful for body content authored directly in .astro, outside the Markdown pipeline.

Usage

Usage

Register the integration in astro.config.mjs. That alone makes Markdown / MDX content tate-chu-yoko-aware.

astro.config.mjsts
// astro.config.mjs
import { defineConfig } from "astro/config";
import tcy from "@love-rox/tcy-astro";

export default defineConfig({
  integrations: [tcy()],
});

With options

Forward any rehype-tcy options through the integration call.

astro.config.mjs (with options)ts
// astro.config.mjs
import { defineConfig } from "astro/config";
import tcy from "@love-rox/tcy-astro";

export default defineConfig({
  integrations: [
    tcy({
      maxLength: 2,
      excludeWords: ["URL", "API", "2026"],
    }),
  ],
});

Inside an `.astro` file

For body content authored directly in .astro (no Markdown in the picture), use the <Tcy> component.

src/pages/example.astroastro
---
import Tcy from "@love-rox/tcy-astro/Tcy.astro";
---

<p style="writing-mode: vertical-rl">
  <Tcy>第1章 2026年4月、Webの縦書きは進化した。</Tcy>
</p>

Options

Options

The shared options behave exactly as they do in the React / Vue / rehype adapters. There's just one integration-only knob.

Shared options + rehype passthrough

OptionTypeDefaultDescription
target'alphanumeric' | 'alpha' | 'digit' | 'ascii' | RegExp'alphanumeric'What counts as a tate-chu-yoko target. alphanumeric matches [0-9A-Za-z]; ascii covers printable ASCII. A custom RegExp is accepted.
combinebooleantrueMerge consecutive target characters into a single span. Set to false to wrap each character individually.
includestring | string[]undefinedExtra characters to treat as targets regardless of target.
excludestring | string[]undefinedCharacters to exclude. Takes precedence over include.
maxLengthnumberundefinedMaximum length for a tcy segment. Runs longer than this are demoted back to plain text — for example maxLength: 2 keeps single digits and pairs uprighted while leaving four-digit years like 2026 lying on their side.
excludeWordsstring[]undefinedExact words to exclude from tcy wrapping. Matched against the whole segment value (not a substring) — useful for keeping acronyms like URL / API or specific years out of the upright treatment.
tagNamestring'span'Tag name used for wrapping.
classNamestring | string[]'tcy'Class name(s) applied to the wrapping element. Pass an array for multiple classes.
skipTagsstring[]['code', 'pre', 'script', 'style']Tags whose subtrees are left untouched. Code/pre/script/style content is skipped by default.

Integration-only

OptionTypeDefaultDescription
markdownbooleantrueWhether to register rehype-tcy on the Markdown / MDX pipeline. Set to false to opt out (e.g. if you only want the <Tcy> component).

Behaviour

Behavior notes

  • Astro is SSG-first, so both pieces run at build time — there's no client-side cost.
  • Text inside <code> / <pre> / <script> / <style> is skipped by default (configurable via skipTags).
  • Idempotent. Rebuild the same Markdown as many times as you want; the output HTML doesn't drift.
  • The <Tcy> component delegates to the same tcy-rehype underneath, so Markdown content and .astro-authored content behave identically.

Related

Other adapters

Not using Astro? Drop the rehype plugin directly into your unified pipeline. Need runtime wrapping in the app? Use the React or Vue component.