> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/BankkRoll/tweakcn-theme-picker/llms.txt
> Use this file to discover all available pages before exploring further.

# Remix Installation

> Install and configure tweakcn themes in your Remix application

## Prerequisites

Before installing themes, ensure you have shadcn/ui set up in your Remix project. If you haven't already:

<Steps>
  <Step title="Initialize shadcn/ui">
    ```bash theme={null}
    npx shadcn@latest init
    ```
  </Step>

  <Step title="Install required components">
    The theme system requires these shadcn/ui components:

    ```bash theme={null}
    npx shadcn@latest add dropdown-menu button
    ```
  </Step>
</Steps>

## Installation

<Steps>
  <Step title="Install the theme system">
    Install the complete theme system with all 40+ themes:

    <CodeGroup>
      ```bash npm theme={null}
      npx shadcn@latest add https://tweakcn-picker.vercel.app/r/remix/theme-system
      ```

      ```bash yarn theme={null}
      yarn dlx shadcn@latest add https://tweakcn-picker.vercel.app/r/remix/theme-system
      ```

      ```bash pnpm theme={null}
      pnpm dlx shadcn@latest add https://tweakcn-picker.vercel.app/r/remix/theme-system
      ```
    </CodeGroup>

    This installs:

    * `lib/themes-config.ts` - Theme configuration and metadata
    * `lib/sessions.server.ts` - Server-side session management
    * `components/mode-toggle.tsx` - Theme toggle component
    * `app/routes/action.set-theme.ts` - Theme action route
    * `styles/themes/*.css` - All 40+ theme CSS files
    * `remix-themes` dependency for SSR theme support
  </Step>

  <Step title="Configure theme session in root loader">
    Update your `app/root.tsx` to add the theme session resolver:

    ```tsx app/root.tsx theme={null}
    import {
      Links,
      Meta,
      Outlet,
      Scripts,
      ScrollRestoration,
      useLoaderData,
    } from "@remix-run/react";
    import { LoaderFunctionArgs } from "@remix-run/node";
    import { themeSessionResolver } from "@/lib/sessions.server";
    import { PreventFlashOnWrongTheme, ThemeProvider, useTheme } from "remix-themes";
    import "@/styles/themes/index.css";

    export async function loader({ request }: LoaderFunctionArgs) {
      const { getTheme } = await themeSessionResolver(request);
      return {
        theme: getTheme(),
      };
    }

    export default function AppWithProviders() {
      const data = useLoaderData<typeof loader>();
      return (
        <ThemeProvider specifiedTheme={data.theme} themeAction="/action/set-theme">
          <App />
        </ThemeProvider>
      );
    }

    function App() {
      const data = useLoaderData<typeof loader>();
      const [theme] = useTheme();
      
      return (
        <html lang="en" data-theme={theme ?? ""}>
          <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <Meta />
            <PreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} />
            <Links />
          </head>
          <body>
            <Outlet />
            <ScrollRestoration />
            <Scripts />
          </body>
        </html>
      );
    }
    ```
  </Step>

  <Step title="Add the ModeToggle to your UI">
    Import and use the ModeToggle component anywhere in your app:

    ```tsx app/components/Header.tsx theme={null}
    import { ModeToggle } from "@/components/mode-toggle";

    export function Header() {
      return (
        <header>
          <nav>
            {/* Your navigation */}
            <ModeToggle />
          </nav>
        </header>
      );
    }
    ```
  </Step>

  <Step title="Update session secret (production)">
    <Warning>
      Replace the default session secret in `lib/sessions.server.ts` before deploying to production:
    </Warning>

    ```typescript lib/sessions.server.ts theme={null}
    const sessionStorage = createCookieSessionStorage({
      cookie: {
        name: "tweakcn-theme",
        path: "/",
        httpOnly: true,
        sameSite: "lax",
        secrets: [process.env.SESSION_SECRET || "s3cr3t"], // Use environment variable!
        ...(isProduction
          ? { domain: "your-production-domain.com", secure: true }
          : {}),
      },
    });
    ```

    Add `SESSION_SECRET` to your `.env` file:

    ```bash .env theme={null}
    SESSION_SECRET=your-very-secret-random-string-here
    ```
  </Step>
</Steps>

## How it works

The Remix adapter uses **remix-themes** for server-side rendering with cookie-based session storage:

<AccordionGroup>
  <Accordion title="Session configuration">
    Located at `lib/sessions.server.ts`:

    ```typescript theme={null}
    import { createCookieSessionStorage } from "@remix-run/node";
    import { createThemeSessionResolver } from "remix-themes";

    const isProduction = process.env.NODE_ENV === "production";

    const sessionStorage = createCookieSessionStorage({
      cookie: {
        name: "tweakcn-theme",
        path: "/",
        httpOnly: true,
        sameSite: "lax",
        secrets: ["s3cr3t"], // Replace in production!
        ...(isProduction
          ? { domain: "your-production-domain.com", secure: true }
          : {}),
      },
    });

    export const themeSessionResolver = createThemeSessionResolver(sessionStorage);
    ```

    Themes are stored in HTTP-only cookies, making them available on the server for SSR.
  </Accordion>

  <Accordion title="Theme action route">
    Located at `app/routes/action.set-theme.ts`:

    ```typescript theme={null}
    import { createThemeAction } from "remix-themes";
    import { themeSessionResolver } from "@/lib/sessions.server";

    export const action = createThemeAction(themeSessionResolver);
    ```

    This route handles theme changes from the client. The `remix-themes` package automatically creates the action handler.
  </Accordion>

  <Accordion title="ModeToggle component">
    Located at `components/mode-toggle.tsx`:

    ```tsx theme={null}
    import { Moon, Sun } from "lucide-react";
    import { Theme, useTheme } from "remix-themes";
    import { Button } from "@/components/ui/button";
    import {
      DropdownMenu,
      DropdownMenuContent,
      DropdownMenuItem,
      DropdownMenuTrigger,
    } from "@/components/ui/dropdown-menu";

    export function ModeToggle() {
      const [theme, setTheme] = useTheme();

      return (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="ghost" size="icon">
              <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
              <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
              <span className="sr-only">Toggle theme</span>
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuItem onClick={() => setTheme(Theme.LIGHT)}>
              Light
            </DropdownMenuItem>
            <DropdownMenuItem onClick={() => setTheme(Theme.DARK)}>
              Dark
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      );
    }
    ```
  </Accordion>

  <Accordion title="Preventing flash of wrong theme">
    The `PreventFlashOnWrongTheme` component prevents FOUC by applying the theme before React hydrates:

    ```tsx theme={null}
    <head>
      <PreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} />
    </head>
    ```

    This component injects an inline script that runs immediately, setting the theme before any content renders.
  </Accordion>
</AccordionGroup>

## Adding individual themes

To install specific themes instead of all 40+:

<CodeGroup>
  ```bash GitHub theme theme={null}
  npx shadcn@latest add https://tweakcn-picker.vercel.app/r/theme-github
  ```

  ```bash Multiple themes theme={null}
  npx shadcn@latest add https://tweakcn-picker.vercel.app/r/theme-github
  npx shadcn@latest add https://tweakcn-picker.vercel.app/r/theme-stripe
  npx shadcn@latest add https://tweakcn-picker.vercel.app/r/theme-spotify
  ```
</CodeGroup>

Then import only the themes you need:

```css app/styles/themes/index.css theme={null}
@import "./github.css";
@import "./stripe.css";
@import "./spotify.css";
```

## Customizing themes

All theme CSS files are in `app/styles/themes/`. Each theme defines CSS variables:

```css app/styles/themes/github.css theme={null}
[data-theme="github-light"] {
  --background: oklch(1 0 0);
  --foreground: oklch(0.23 0.01 264.53);
  --primary: oklch(0.52 0.16 145);
  --primary-foreground: oklch(1 0 0);
  /* ... more variables */
}

[data-theme="github-dark"] {
  --background: oklch(0.13 0.01 264.53);
  --foreground: oklch(0.93 0.01 264.53);
  --primary: oklch(0.62 0.16 145);
  --primary-foreground: oklch(0.13 0.01 264.53);
  /* ... more variables */
}
```

Edit these files to customize colors, borders, shadows, and more.

## TypeScript usage

Use the `useTheme` hook from remix-themes:

```typescript theme={null}
import { Theme, useTheme } from "remix-themes";

function MyComponent() {
  const [theme, setTheme] = useTheme();
  
  return (
    <div>
      <p>Current theme: {theme}</p>
      <button onClick={() => setTheme(Theme.DARK)}>
        Switch to Dark
      </button>
      <button onClick={() => setTheme(Theme.LIGHT)}>
        Switch to Light
      </button>
    </div>
  );
}
```

## Server-side theme access

Access the current theme in loaders and actions:

```typescript theme={null}
import { LoaderFunctionArgs } from "@remix-run/node";
import { themeSessionResolver } from "@/lib/sessions.server";

export async function loader({ request }: LoaderFunctionArgs) {
  const { getTheme } = await themeSessionResolver(request);
  const currentTheme = getTheme();
  
  // Use theme for server-side logic
  return { theme: currentTheme };
}
```

## Production deployment

Before deploying to production:

<Steps>
  <Step title="Set session secret">
    Add a strong session secret to your environment:

    ```bash theme={null}
    SESSION_SECRET=$(openssl rand -base64 32)
    ```
  </Step>

  <Step title="Configure domain">
    Update the cookie domain in `lib/sessions.server.ts`:

    ```typescript theme={null}
    ...(isProduction
      ? { domain: "yourdomain.com", secure: true }
      : {}),
    ```
  </Step>

  <Step title="Enable secure cookies">
    Ensure `secure: true` is set for production to require HTTPS.
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Browse Themes" icon="palette" href="https://tweakcn-picker.vercel.app">
    Explore all 40+ available themes
  </Card>

  <Card title="Theme Picker" icon="eye-dropper" href="https://tweakcn-picker.vercel.app">
    Preview themes in real-time
  </Card>

  <Card title="Next.js Setup" icon="forward" href="/guides/nextjs">
    Install themes in Next.js
  </Card>

  <Card title="Astro Setup" icon="rocket" href="/guides/astro">
    Install themes in Astro
  </Card>
</CardGroup>
