Read this in other languages: 中文.
A modern full-stack React application based on TanStack Start that demonstrates how to implement a complete light/dark theme switching functionality. This project provides a plug-and-play theme system that can be easily integrated into other projects.
- 🌓 Complete theme system - Supports light/dark/system themes with automatic system preference detection
- 🚀 Modern tech stack - Built on React 19, TanStack Start, Tailwind CSS v4
- 🎨 Beautiful UI components - Integrated shadcn/ui component library with New York style
- ⚡ Flicker-free switching - Elegant theme switching animations that prevent page flickering
- 📱 Responsive design - Mobile-first responsive layout
- 🔧 Full TypeScript coverage - Complete type safety
- 🌳 SSR/SSG support - Full server-side rendering support
- Framework: TanStack Start (full-stack React framework)
- Routing: TanStack Router
- Styling: Tailwind CSS
- UI Components: shadcn/ui
- Icons: Lucide React
- Build Tools: Vite + TypeScript
# Clone the project
git clone <repository-url>
cd tanstack-start-theme
# Install dependencies
pnpm install
# Start development server
pnpm devVisit http://localhost:3000 to see the demo.
tanstack-start-theme/
├── src/
│ ├── components/
│ │ ├── theme.tsx # 🌟 Theme system core file (directly reusable)
│ │ ├── Header.tsx # Page header component
│ │ └── ui/ # shadcn/ui component library
│ ├── routes/
│ │ ├── __root.tsx # 🌟 Root route component (needs modification)
│ │ └── index.tsx # Homepage component
│ ├── router.tsx # 🌟 Router configuration (needs modification)
│ ├── styles.css # 🌟 Global styles and theme variables
│ └── lib/utils.ts # Utility functions
├── components.json # shadcn/ui configuration
└── README.md
The following files can be directly copied to other projects:
# Theme system core
cp src/components/theme.tsx your-project/src/components/The following files need to be adapted according to your project:
In your project, you need to integrate the ThemeProvider into the routing system:
// src/router.tsx
import { ThemeProvider } from '@/components/theme'
export const getRouter = () => {
const router = createRouter({
routeTree,
scrollRestoration: true,
defaultPreloadStaleTime: 0,
Wrap: (props: { children: React.ReactNode }) => {
return <ThemeProvider>{props.children}</ThemeProvider>
}
})
return router
}Add anti-flicker script:
// src/routes/__root.tsx
export const Route = createRootRoute({
// ... route configuration
shellComponent: RootDocument,
})
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<HeadContent />
{/* 🌟 Key: Anti-flicker script */}
<script>
{`
if (typeof window !== 'undefined') {
let isDark = localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
document.documentElement.classList.toggle('dark', isDark);
}
`}
</script>
</head>
<body>
<Header />
{children}
<Scripts />
</body>
</html>
)
}import { useTheme, ThemeToggle } from '@/components/theme'
function MyComponent() {
const { theme, setTheme } = useTheme()
return (
<div>
{/* Theme toggle button */}
<ThemeToggle />
{/* Programmatic switch */}
<button onClick={() => setTheme('dark')}>
Switch to dark mode
</button>
</div>
)
}- Isomorphic functions - Use
createIsomorphicFnfor server/client compatibility - Local storage - Use localStorage to persist user preferences
- System theme - Support following system theme settings
- Reactive switching - Listen to system theme changes and update in real-time
Theme context provider, needs to wrap the application root:
<ThemeProvider>
<App />
</ThemeProvider>Theme state Hook:
const { theme, setTheme, systemTheme } = useTheme()
// theme: 'light' | 'dark' | 'system' - current theme
// setTheme: (theme: Theme) => void - set theme
// systemTheme: 'light' | 'dark' - system themeReady-to-use theme toggle button component:
<ThemeToggle />- Zero flicker - Apply theme immediately on page load to avoid visual flicker
- SSR friendly - Full server-side rendering support
- Type safe - Complete TypeScript support
- Performance optimized - CSS Variables-based theme switching
- Easy integration - Minimal configuration, plug and play
- Modern - Using latest web standards and best practices
Issues and Pull Requests are welcome!
MIT License