Skip to content

docs(dark-mode): add tanstack start guide#10396

Merged
shadcn merged 4 commits intoshadcn-ui:mainfrom
ramonclaudio:docs/dark-mode-tanstack-start
Apr 21, 2026
Merged

docs(dark-mode): add tanstack start guide#10396
shadcn merged 4 commits intoshadcn-ui:mainfrom
ramonclaudio:docs/dark-mode-tanstack-start

Conversation

@ramonclaudio
Copy link
Copy Markdown
Contributor

@ramonclaudio ramonclaudio commented Apr 14, 2026

Adds a dark mode guide for TanStack Start, the only shadcn-CLI-supported framework without one.

This is the official TanStack pattern. Their document-head-management guide says:

For scripts that must run before React hydrates (like theme detection), use ScriptOnce.

That guide ships with a ThemeProvider example wrapping <ScriptOnce>{themeScript}</ScriptOnce> around {children}, with a localStorage + matchMedia script. tanstack.com runs the same pattern in production: an inline pre-hydration script that reads localStorage, falls back to matchMedia('(prefers-color-scheme: dark)'), and applies the class to documentElement (tanstack.com/__root.tsx L107-L113).

This PR is that recipe with three production additions: useState(defaultTheme) instead of reading localStorage directly (so SSR and client match), document.documentElement.style.colorScheme (so native UI respects the theme), and a prefers-color-scheme listener for "system" mode.

Same three-step shape as the Vite guide: provider, root layout, toggle. No server functions, no cookies, no third-party deps. Just ScriptOnce + React Context + localStorage.

Note: TanStack's other doc, integrate-shadcn-ui, is out of date. Its ThemeProvider initializes useState from localStorage directly (L441-L443), which throws under SSR. This PR aligns shadcn's docs with the canonical document-head-management recipe instead.

Why not adapt one of the existing guides?

What's been tried

@shadcn commented on #7173 and #7490 asking how they compare. All four, verified against the file diffs (and the tanstack-theme-kit source for #9096):

#7173 #7490 #9096 This PR
ScriptOnce no only on theme === 'system' no (library inlines own <script>) always
System mode no yes yes yes
prefers-color-scheme listener no no yes yes
Sets colorScheme no no yes yes
suppressHydrationWarning no no yes yes
Persistence cookies + createServerFn cookies + createServerFn localStorage via library localStorage
Third-party dep none none tanstack-theme-kit none
Targets apps/v4/ no (apps/www/) no (apps/www/) yes yes

#7173 and #7490 use cookies plus a createServerFn, so the server renders the class directly on <html> (no suppressHydrationWarning needed). #7173 has no system mode (cookie defaults to "light"). #7490 adds system mode but only fires ScriptOnce when the stored value is "system". Neither sets colorScheme or listens for OS changes.

#9096 wraps tanstack-theme-kit, which does most of the right things at the cost of a runtime dep and a custom inline <script> rather than ScriptOnce.

#7055 (March 2025) closed with "use next-themes" after a commenter hit localStorage is not defined using the Vite guide.

Sources

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 14, 2026

@ramonclaudio is attempting to deploy a commit to the shadcn-pro Team on Vercel.

A member of the Team first needs to authorize it.

ramonclaudio added a commit to ramonclaudio/ramonclaudio.com that referenced this pull request Apr 20, 2026
- document the canonical ScriptOnce + Context pattern for dark mode in TanStack Start
- compare against the three open shadcn/ui PRs (#7173, #7490, #9096) and the Vite guide's open PRs
- link to shadcn-ui/ui#10396 (the PR), ramonclaudio/tanstack-cn (starter repo), tanstack-cn + create-tanstack-cn (npm), and tanstack-cn.vercel.app (live demo)
ramonclaudio added a commit to ramonclaudio/patches that referenced this pull request Apr 20, 2026
source patch for shadcn-ui/ui#10396 adding TanStack Start as a fifth
dark mode guide alongside Next.js, Vite, Astro, and Remix. ScriptOnce +
Context pattern with SSR-safe useState, suppressHydrationWarning,
colorScheme, and OS preference listener. Three files: new MDX guide,
index card, meta.json entry. Apply with git apply in a shadcn-ui/ui
clone.

Companion starter at ramonclaudio/tanstack-cn. Walkthrough at
ramonclaudio.com/posts/tanstack-start-dark-mode.
@shadcn
Copy link
Copy Markdown
Collaborator

shadcn commented Apr 21, 2026

I added getThemeScript because ScriptOnce requires a string, but that string needs to stay in sync with the provider props. Let me know what you think. Otherwise good to merge.

Thank @ramonclaudio

@shadcn shadcn added the area: roadmap This looks great. We'll add it to the roadmap, review and merge. label Apr 21, 2026
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ui Ready Ready Preview, Comment Apr 21, 2026 9:14am

Request Review

@ramonclaudio
Copy link
Copy Markdown
Contributor Author

Yup, hardcoding theme and system meant custom storageKey or defaultTheme would silently break FOUC prevention. Looks good! @shadcn

@shadcn shadcn merged commit d0ac558 into shadcn-ui:main Apr 21, 2026
7 checks passed
@ramonclaudio ramonclaudio deleted the docs/dark-mode-tanstack-start branch April 21, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: roadmap This looks great. We'll add it to the roadmap, review and merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants