exequiel-sosa
alal
← Back to blog
[performance]August 3, 2026· 3 min read

Mastering CSS writing-mode in React: practical patterns for vertical text layouts

Learn how to use CSS writing-mode in React and Next.js projects. Real‑world examples, performance tips, and trade‑offs for vertical text in modern web apps.

#css#writing-mode#react#nextjs#accessibility

Why writing-mode matters in production UI

When you need vertical text—think Japanese sidebars, decorative headings, or data tables with rotated labels—writing-mode is the cleanest solution. It avoids the transform: rotate() hacks that break accessibility and line‑height calculations. In a large Next.js codebase, a consistent approach saves you from layout bugs that surface only on high‑DPI screens.

Basic usage in a component

In React you can apply the property directly via style or a CSS module. The latter is preferred because it keeps the CSS static for the optimizer.

// components/VerticalLabel.tsx
import styles from './VerticalLabel.module.css';

export default function VerticalLabel({text}: {text: string}) {
  return <span className={styles.vertical}>{text}</span>;
}
/* VerticalLabel.module.css */
.vertical {
  writing-mode: vertical-rl; /* right‑to‑left vertical */
  text-orientation: mixed; /* keep latin upright */
  padding: 0.5rem;
}

This renders the string vertically while keeping Latin characters readable. The text-orientation switch is often overlooked; without it, letters appear rotated, which hurts readability.

Performance considerations

Browsers treat writing-mode as a layout property, so it triggers a re‑flow when toggled. In a table with hundreds of cells, switching modes on hover can cause jank. The safe pattern is to set the mode once at mount and avoid dynamic changes. If you need a hover effect, use transform: rotate() on a child element instead of flipping the whole block.

SEO and accessibility

Screen readers read vertical text in logical order, but you must still provide a proper lang attribute on the container. For mixed‑language content, wrap the vertical portion in a <span lang="ja"> so the voice engine switches correctly. Also, avoid using writing-mode for purely decorative text; aria-hidden="true" prevents unnecessary verbosity.

Common pitfalls and how to avoid them

  • Overflow hidden: vertical blocks often exceed container height. Use overflow-x:auto on the parent to enable scrolling.
  • Flexbox incompatibility: flex containers don’t respect the block‑direction of writing-mode. Switch to display:inline-block for the vertical element or use CSS Grid.
  • Browser support quirks: Safari requires the -webkit- prefix for older versions. Include it in your fallback CSS.

Putting it together in Next.js

Next.js static generation means the CSS is extracted at build time. By keeping writing-mode in a module, the CSS is deduped and cached. Here’s a minimal page that shows a vertical navigation on the left side.

// pages/vertical-nav.tsx
import VerticalLabel from '@/components/VerticalLabel';

export default function VerticalNav() {
  return (
    <nav style={{display:'flex'}}>
      <VerticalLabel text="メニュー" />
      <ul style={{marginLeft:'1rem'}}>
        <li>Home</li>
        <li>About</li>
        <li>Contact</li>
      </ul>
    </nav>
  );
}

The vertical label stays crisp, the rest of the layout uses normal flexbox, and the page ships with a single CSS bundle. No runtime calculations, no layout thrash.

// related

find me in:
linkedin
X
facebook