Skip to main content

Astro Setup

Step-by-step guide to set up Ninna UI in an Astro 5 project with React integration and Tailwind CSS v4. Use Ninna UI's React components inside Astro's island architecture.

Prerequisites

  • Node.js 18+
  • Astro 5
  • @astrojs/react integration
  • Tailwind CSS v4 via @tailwindcss/vite

Create Project

Skip this step if you already have an Astro project.

npm create astro@latest my-app
cd my-app

Install Ninna UI

Install the component packages you need. All packages auto-install @ninna-ui/core.

pnpm add @ninna-ui/primitives @ninna-ui/feedback @ninna-ui/forms @ninna-ui/layout @astrojs/react

You can also install additional packages later — e.g. @ninna-ui/overlays, @ninna-ui/navigation, @ninna-ui/data-display.

Install Tailwind CSS

Astro uses Vite under the hood — use the @tailwindcss/vite plugin for the fastest builds.

pnpm add
pnpm add -D tailwindcss @tailwindcss/vite

Configure Astro

Add the React integration and Tailwind CSS plugin to your Astro config.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
integrations: [react()],
vite: {
plugins: [tailwindcss()],
},
});

CSS Setup

Create your global styles file.

Create src/styles/globals.css:

@import "tailwindcss";
@import "@ninna-ui/core/theme/presets/default.css";
/* Enable dark mode support */
@variant dark (&:is(.dark *));

Layout Setup

Create a base layout that imports your styles and sets the data-theme attribute.

---
// src/layouts/Layout.astro
import "../styles/globals.css";
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="en" data-theme="default">
<head>
<meta charset="UTF-8" />
<meta name="description" content="My Ninna UI + Astro app" />
<meta name="viewport" content="width=device-width" />
<title>{title}</title>
</head>
<body class="min-h-screen bg-base-50 text-base-content antialiased">
<slot />
</body>
</html>

The data-theme attribute must match the preset name from your CSS import.

Your First Component

Create a React component and use it in an Astro page.

React components must be rendered with a client: directive to be interactive:

// src/components/Hero.tsx
import { Button, Heading, Text } from "@ninna-ui/primitives";
export function Hero() {
return (
<div className="p-8 text-center">
<Heading as="h1" size="3xl">Hello Ninna UI + Astro</Heading>
<Text className="text-base-content/70 mt-2">It works!</Text>
<Button color="primary" className="mt-4">Get Started</Button>
</div>
);
}
---
// src/pages/index.astro
import Layout from '../layouts/Layout.astro';
import { Hero } from '../components/Hero';
---
<Layout title="My App">
<Hero client:load />
</Layout>

Use client:load for interactive components, or client:idle / client:visible for deferred hydration.

Vite Version Note

Astro 5 requires Vite 7. Pin the version to avoid warnings.

Add overrides to your package.json to ensure Vite 7 is used across all package managers:

{
"overrides": { "vite": "^7.0.0" },
"resolutions": { "vite": "^7.0.0" },
"pnpm": { "overrides": { "vite": "^7.0.0" } }
}

Theme Presets

Switch themes by changing the CSS import and the data-theme attribute.

/* 1. Change the CSS import in src/styles/globals.css */
@import "@ninna-ui/core/theme/presets/default.css"; /* Electric purple */
@import "@ninna-ui/core/theme/presets/ocean.css"; /* Blue + cyan */
@import "@ninna-ui/core/theme/presets/sunset.css"; /* Orange + rose */
@import "@ninna-ui/core/theme/presets/forest.css"; /* Green + amber */
@import "@ninna-ui/core/theme/presets/minimal.css"; /* Monochrome */
/* 2. Update data-theme in src/layouts/Layout.astro */
/* <html lang="en" data-theme="ocean"> */

Dark mode works automatically via prefers-color-scheme or by adding the .dark class to <html>.