Skip to content

fix(useTextDirection): read the document direction on the first render - #230

Merged
childrentime merged 1 commit into
childrentime:mainfrom
rawsun007:fix/text-direction-initial-read
Sep 17, 2026
Merged

childrentime merged 1 commit into
childrentime:mainfrom
rawsun007:fix/text-direction-initial-read

Conversation

@rawsun007

Copy link
Copy Markdown
Contributor

Description

useTextDirection destructures initialValue with a default:

const { selector = 'html', initialValue = 'ltr' } = options
const getValue = () => {
  if (initialValue !== undefined) {
    return initialValue          // always taken
  }
  if (isBrowser) { ...read the dir attribute... }   // dead
  ...server-side warning...                          // dead
}

Because of the default, initialValue !== undefined is always true, so the branch that reads the element's dir and the SSR warning below it are both unreachable.

The effect corrects the value after mount, so what is left is a wrong first render. Recording the value on every render, on a document with dir="rtl":

before: ["ltr", "rtl"]
after:  ["rtl"]

That is a flash on the client and, under SSR, markup that says ltr for an RTL document — which is the mismatch the dead warning was written to prevent.

Now the document wins, falling back to initialValue and then 'ltr', which is what the unreachable branch already did and matches VueUse's useTextDirection. initialValue still defaults to 'ltr' in effect, so nothing changes for a document that sets no dir.

Type of Change

  • Bug fix
  • New hook
  • Enhancement to existing hook
  • Documentation update
  • Other (please describe)

Checklist

  • I have read the Contributing Guide
  • I have read and understood every line of this diff myself, including any AI-written parts (AI usage policy)
  • My code follows the project's coding style
  • I have added tests for my changes
  • All existing tests pass
  • I have updated the documentation

The hook had no spec; this adds five. Mutation check: reverting only index.ts fails exactly the two that pin the new behaviour (first render on an RTL document, and document-over-initialValue), while the three that describe unchanged behaviour stay green.

jest 427 passing, eslint clean, tsc --noEmit clean. No interface.ts change, so no docs regeneration — @defaultValue 'ltr' is still accurate, it is now the fallback when the element carries no dir.

The diff was written by Claude Opus 5 in Claude Code under my direction; I have not read it line by line myself yet, so that box stays unticked until I have.

🤖 Generated with Claude Code

`initialValue` was destructured with a default of `'ltr'`, so the
`initialValue !== undefined` guard was always true and the branch that
reads the element's `dir` attribute could never run. On a document with
`dir="rtl"` the hook rendered `ltr` first and only corrected after the
effect, and the server-side warning behind the same guard was dead code.

The document now wins, with `initialValue` and then `'ltr'` as fallbacks,
which is also what the unreachable branch was written to do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@childrentime childrentime left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally: reverting only index.ts fails exactly the two specs that pin the new behaviour, and the SSR path now emits the warning it was always meant to. Thanks!

@childrentime
childrentime merged commit 63dc2be into childrentime:main Sep 17, 2026
4 checks passed
childrentime added a commit that referenced this pull request Sep 17, 2026
Since #230 the target element's `dir` wins over `initialValue`, so the option
is a fallback used only when the element carries no `dir` — not the initial
value the table still described. Regenerated through `gend`.

The notes also cover the consequence for server rendering: the client reads
the element on its first render, so an app that server-renders has to pass an
`initialValue` matching its markup, or the two disagree and React reports a
hydration mismatch. The hook warns about exactly this again now that #230 made
the warning reachable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants