React adapter
@love-rox/tcy-react
A `<Tcy>` component for React 17+. Walks children recursively and wraps half-width alphanumeric runs in `<span class="tcy">`. Produces deterministic, SSR-safe output.
v0.3.0MITSSR-safe
Install
@love-rox/tcy-core comes along as a dependency — no separate install needed.
bun
bun add @love-rox/tcy-reactpnpm
pnpm add @love-rox/tcy-reactnpm
npm i @love-rox/tcy-reactyarn
yarn add @love-rox/tcy-reactUsage
Wrap the vertical content in <Tcy> inside an element with writing-mode: vertical-rl. Only string children are rewritten; element children are walked recursively without being touched.
Reacttsx
import { Tcy } from "@love-rox/tcy-react";
export function Chapter() {
return (
<p style={{ writingMode: "vertical-rl" }}>
<Tcy>第1章 2026年4月、Webの縦書きは進化した。</Tcy>
</p>
);
}Options
<Tcy> accepts the options below. The shared options behave identically to the Vue, rehype, and Astro adapters.
Shared options
| Option | Type | Default | Description |
|---|---|---|---|
| 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. |
| combine | boolean | true | Merge consecutive target characters into a single span. Set to false to wrap each character individually. |
| include | string | string[] | undefined | Extra characters to treat as targets regardless of target. |
| exclude | string | string[] | undefined | Characters to exclude. Takes precedence over include. |
| maxLength | number | undefined | Maximum 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. |
| excludeWords | string[] | undefined | Exact 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. |
Component-only props
| Option | Type | Default | Description |
|---|---|---|---|
| className | string | 'tcy' | Class applied to each generated span. |
| as | keyof JSX.IntrinsicElements | 'span' | Tag name used for wrapping. |
Behavior notes
- Runs are not joined across element boundaries —
<em>12</em>34produces two separate spans. - Full-width alphanumerics (
A–Z/0–9) are left alone because they already render upright in vertical text. - SSR-safe — server and client emit the same DOM, so there's no hydration mismatch.
- Children that contain React elements are still walked recursively; text inside those elements is processed too.
Other adapters
Using Vue? The Vue adapter has the same API. On a Markdown / Astro pipeline? The rehype and Astro adapters do this at build time, with no React dependency in the bundle.