> ## 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.

# Astro Installation

> Install and configure tweakcn themes in your Astro application

## Prerequisites

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

<Steps>
  <Step title="Add React to Astro">
    ```bash theme={null}
    npx astro add react
    ```
  </Step>

  <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/astro/theme-system
      ```

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

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

    This installs:

    * `lib/themes-config.ts` - Theme configuration and metadata
    * `components/theme-script.astro` - Inline script for preventing FOUC
    * `components/mode-toggle.tsx` - React component for theme switching
    * `styles/themes/*.css` - All 40+ theme CSS files
  </Step>

  <Step title="Add theme script to your layout">
    Add the theme script component in the `<head>` of your base layout to prevent flash of unstyled content (FOUC):

    ```astro src/layouts/Layout.astro theme={null}
    ---
    import ThemeScript from "@/components/theme-script.astro";
    import "@/styles/themes/index.css";
    ---

    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>My Astro Site</title>
        <ThemeScript />
      </head>
      <body>
        <slot />
      </body>
    </html>
    ```

    <Warning>
      The `ThemeScript` component must be placed in the `<head>` tag to work correctly and prevent FOUC.
    </Warning>
  </Step>

  <Step title="Add the ModeToggle to your UI">
    Import and use the ModeToggle component in any `.astro` or `.tsx` file:

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

    <header>
      <nav>
        <!-- Your navigation -->
        <ModeToggle client:load />
      </nav>
    </header>
    ```

    <Note>
      The `client:load` directive is required for React components to be interactive in Astro.
    </Note>
  </Step>
</Steps>

## How it works

The Astro adapter uses an **inline script** for theme management to avoid hydration issues:

<AccordionGroup>
  <Accordion title="Theme Script implementation">
    Located at `components/theme-script.astro`:

    ```astro theme={null}
    <script is:inline>
      const STORAGE_KEY = "tweakcn-theme";
      const DEFAULT_THEME = "default-dark";

      function getThemePreference() {
        if (typeof localStorage !== "undefined" && localStorage.getItem(STORAGE_KEY)) {
          return localStorage.getItem(STORAGE_KEY);
        }
        return DEFAULT_THEME;
      }

      function applyTheme(theme) {
        document.documentElement.setAttribute("data-theme", theme);
      }

      // Apply theme immediately
      const theme = getThemePreference();
      applyTheme(theme);

      // Listen for theme changes from React components
      if (typeof window !== "undefined") {
        window.setTheme = function(newTheme) {
          localStorage.setItem(STORAGE_KEY, newTheme);
          applyTheme(newTheme);
          window.dispatchEvent(new CustomEvent("theme-change", { detail: newTheme }));
        };
      }

      // Watch for storage changes (multi-tab sync)
      window.addEventListener("storage", (e) => {
        if (e.key === STORAGE_KEY && e.newValue) {
          applyTheme(e.newValue);
        }
      });
    </script>
    ```

    The `is:inline` directive ensures the script runs before any other JavaScript, preventing FOUC.
  </Accordion>

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

    ```tsx theme={null}
    import * as React from "react";
    import { Moon, Sun } from "lucide-react";
    import { Button } from "@/components/ui/button";

    const STORAGE_KEY = "tweakcn-theme";
    const DEFAULT_THEME = "default-dark";

    export function ModeToggle() {
      const [theme, setThemeState] = React.useState<string>(DEFAULT_THEME);

      React.useEffect(() => {
        // Get initial theme
        const currentTheme =
          document.documentElement.getAttribute("data-theme") ||
          localStorage.getItem(STORAGE_KEY) ||
          DEFAULT_THEME;
        setThemeState(currentTheme);

        // Listen for theme changes
        const handleThemeChange = (e: CustomEvent<string>) => {
          setThemeState(e.detail);
        };

        window.addEventListener("theme-change", handleThemeChange as EventListener);
        return () => {
          window.removeEventListener("theme-change", handleThemeChange as EventListener);
        };
      }, []);

      const setTheme = (newTheme: string) => {
        setThemeState(newTheme);
        if (typeof window !== "undefined" && window.setTheme) {
          window.setTheme(newTheme);
        }
      };

      const isDark = theme.endsWith("-dark");
      const colorTheme = theme.replace("-light", "").replace("-dark", "");

      return (
        <Button
          variant="outline"
          size="icon"
          onClick={() => setTheme(`${colorTheme}-${isDark ? "light" : "dark"}`)}
        >
          {isDark ? <Moon /> : <Sun />}
          <span className="sr-only">Toggle theme</span>
        </Button>
      );
    }
    ```
  </Accordion>

  <Accordion title="Multi-tab synchronization">
    The theme script includes multi-tab synchronization using the Storage API:

    ```javascript theme={null}
    window.addEventListener("storage", (e) => {
      if (e.key === STORAGE_KEY && e.newValue) {
        applyTheme(e.newValue);
      }
    });
    ```

    When you change the theme in one tab, all other tabs automatically update.
  </Accordion>
</AccordionGroup>

## Adding individual themes

To install specific themes instead of all 40+:

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

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

Then import only the themes you need:

```css src/styles/themes/index.css theme={null}
@import "./catppuccin.css";
@import "./supabase.css";
@import "./nature.css";
```

## Customizing themes

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

```css src/styles/themes/supabase.css theme={null}
[data-theme="supabase-light"] {
  --background: oklch(0.99 0 0);
  --foreground: oklch(0.25 0.02 267.08);
  --primary: oklch(0.83 0.13 160.91);
  --primary-foreground: oklch(0.20 0.03 267.08);
  /* ... more variables */
}

[data-theme="supabase-dark"] {
  --background: oklch(0.13 0.01 264.53);
  --foreground: oklch(0.93 0.01 264.53);
  --primary: oklch(0.44 0.1 156.76);
  --primary-foreground: oklch(0.93 0.01 264.53);
  /* ... more variables */
}
```

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

## TypeScript usage

You can programmatically change themes using the global `setTheme` function:

```typescript theme={null}
// In any TypeScript file
if (typeof window !== "undefined" && window.setTheme) {
  window.setTheme("supabase-dark");
}
```

Add TypeScript declarations for the global function:

```typescript src/env.d.ts theme={null}
/// <reference types="astro/client" />

declare global {
  interface Window {
    setTheme: (theme: string) => void;
  }
}

export {};
```

## Astro configuration

Ensure your `astro.config.mjs` includes the React integration:

```javascript astro.config.mjs theme={null}
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';

export default defineConfig({
  integrations: [react()],
});
```

## Client directives

When using React components in Astro, choose the appropriate client directive:

* `client:load` - Hydrates immediately on page load (recommended for theme toggle)
* `client:idle` - Hydrates when the main thread is free
* `client:visible` - Hydrates when the component enters the viewport

```astro theme={null}
<!-- Immediate hydration for theme toggle -->
<ModeToggle client:load />

<!-- Lazy hydration for less critical components -->
<ThemeShowcase client:visible />
```

## 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="Vite Setup" icon="bolt" href="/guides/vite">
    Install themes in Vite React
  </Card>
</CardGroup>
