Skip to content

Using with React frameworks

Notes for Next.js, Remix, Vite, TanStack Start, and Astro.

Cynosure is a plain React package — it works with every major React framework. A few framework-specific notes below.

  1. Import the stylesheets once in app/globals.css or at the top of app/layout.tsx:

    import '@arshad-shah/cynosure-tokens/css';
    import '@arshad-shah/cynosure-tokens/css/dark';
    import '@arshad-shah/cynosure-react/styles.css';
  2. Mark the file that hosts ThemeProvider as a client boundary:

    app/providers.tsx
    'use client';
    import { ThemeProvider, TooltipProvider } from '@arshad-shah/cynosure-react';
    export function Providers({ children }) {
    return (
    <ThemeProvider defaultTheme="system">
    <TooltipProvider>{children}</TooltipProvider>
    </ThemeProvider>
    );
    }
  3. Render the theme init script in <head> to avoid a flash on reload:

    import { getThemeInitScript } from '@arshad-shah/cynosure-react';
    export default function RootLayout({ children }) {
    return (
    <html lang="en" suppressHydrationWarning>
    <head>
    <script dangerouslySetInnerHTML={{ __html: getThemeInitScript() }} />
    </head>
    <body>
    <Providers>{children}</Providers>
    </body>
    </html>
    );
    }

suppressHydrationWarning on <html> silences the benign warning that arises from the data-theme attribute being set by the init script before React hydrates.

  • Put the stylesheet imports in root.tsx and export them via links:

    import styles from '@arshad-shah/cynosure-react/styles.css?url';
    import tokens from '@arshad-shah/cynosure-tokens/css?url';
    export const links = () => [
    { rel: 'stylesheet', href: tokens },
    { rel: 'stylesheet', href: styles },
    ];
  • Wrap <Outlet /> in the providers as in the Next.js example.

Plain client-side. Follow the quickstart — no framework-specific changes.

Treat like Next.js. The 'use client' directive is not required, but the theme init script + suppressHydrationWarning pattern applies.

Render Cynosure inside a React island (client:load or client:idle). Keep the island container small so hydration cost stays proportional.

  • Use per-component entries in hot paths: @arshad-shah/cynosure-react/button, @arshad-shah/cynosure-react/combobox, …
  • Root barrel (@arshad-shah/cynosure-react) works fine with modern bundlers (Vite, Next.js with Webpack 5 / Turbopack, Rollup) — sideEffects: false + per-component modules keep tree-shaking effective.

Bundlers pick up the per-component CSS through the ESM imports, so SSR frameworks emit the right <link> tags automatically. If you are building your own SSR pipeline, collect the generated CSS chunks as you would for any Vanilla-Extract library.