Astro Integration

Learn how to use SmoothUI animated React components in Astro projects using islands architecture, Tailwind CSS 4, and Motion.

Last updated: March 7, 2026

Overview

Astro's islands architecture lets you use SmoothUI React components as interactive islands within your static Astro pages. Each SmoothUI component hydrates independently, giving you smooth animations with minimal JavaScript overhead.

How It Works

SmoothUI components are standard React components. Astro renders them server-side and hydrates them on the client using directives like client:load and client:visible. No modifications to the components are needed.

Prerequisites

Before getting started, make sure you have the following:

RequirementMinimum VersionPurpose
Node.js18+Runtime
Astro4.0+Framework
@astrojs/react4.0+React integration for Astro
Tailwind CSS4.0+Styling
Motion12.0+Animations (Framer Motion)

Project Setup

Create a New Astro Project

If you're starting from scratch, create a new Astro project:

pnpm create astro@latest my-smoothui-app

Add the React Integration

SmoothUI components are React components, so you need @astrojs/react:

pnpm astro add react

This automatically updates your astro.config.mjs to include the React integration:

astro.config.mjs
import { defineConfig } from "astro/config";
import react from "@astrojs/react";

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

Set Up Tailwind CSS 4

Install Tailwind CSS and the Astro integration:

pnpm astro add tailwind && pnpm add tailwindcss @tailwindcss/vite

Add the Tailwind CSS Vite 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()],
  },
});

Create your main CSS file and import Tailwind:

src/styles/global.css
@import "tailwindcss";

Import the stylesheet in your layout:

src/layouts/Layout.astro
---
interface Props {
  title: string;
}

const { title } = Astro.props;
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{title}</title>
  </head>
  <body>
    <slot />
  </body>
</html>

<style is:global>
  @import "../styles/global.css";
</style>

Install Motion

SmoothUI animations are powered by Motion (Framer Motion). Install it as a dependency:

pnpm add motion

Configure Path Aliases

SmoothUI components use the @/ path alias. Add it to your tsconfig.json:

tsconfig.json
{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Installing SmoothUI Components

Using the SmoothUI CLI

pnpm dlx smoothui-cli@latest add siri-orb

Using the shadcn CLI

pnpm dlx shadcn@latest add @smoothui/siri-orb

shadcn Configuration

If this is your first time using the shadcn CLI in an Astro project, it will prompt you to create a components.json file. Accept the defaults — the CLI auto-detects Astro projects and configures paths accordingly.

Client Directives

Astro components are static by default. To make SmoothUI components interactive, you need to add a client directive that tells Astro when to hydrate the component.

DirectiveWhen It HydratesBest For
client:loadImmediately on page loadAbove-the-fold components, critical interactions
client:visibleWhen the component scrolls into viewBelow-the-fold components, cards, animations
client:idleWhen the browser is idleNon-critical UI, background animations
client:only="react"Client-only, no SSRComponents that use browser APIs on mount

Choosing the Right Directive

Use client:visible for most SmoothUI components — it gives the best performance since components only hydrate when the user can see them. Use client:load for hero sections or components that must be interactive immediately.

Using Components in Astro Pages

Here is a complete example of using a SmoothUI component in an Astro page:

src/pages/index.astro
---
import Layout from "../layouts/Layout.astro";
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
---

<Layout title="SmoothUI + Astro">
  <main class="flex min-h-screen flex-col items-center justify-center gap-8 p-8">
    <h1 class="text-4xl font-bold">SmoothUI in Astro</h1>

    <!-- Hydrate immediately — above the fold -->
    <SiriOrb client:load size="200px" />

    <!-- Hydrate when visible — below the fold -->
    <section class="mt-32">
      <SiriOrb client:visible size="150px" />
    </section>
  </main>
</Layout>

Using Multiple Components

src/pages/demo.astro
---
import Layout from "../layouts/Layout.astro";
import { MagneticButton } from "@/components/smoothui/ui/MagneticButton";
import { ExpandableCards } from "@/components/smoothui/ui/ExpandableCards";
import { TypewriterText } from "@/components/smoothui/ui/TypewriterText";
---

<Layout title="SmoothUI Components">
  <main class="flex min-h-screen flex-col items-center gap-12 p-8">
    <TypewriterText client:load text="Welcome to SmoothUI" />

    <MagneticButton client:load>
      Get Started
    </MagneticButton>

    <section class="w-full max-w-4xl">
      <ExpandableCards client:visible />
    </section>
  </main>
</Layout>

Motion in Astro Islands

Motion (Framer Motion) works seamlessly inside Astro islands. Since each island is a fully hydrated React component, all animation features are available — spring physics, layout animations, gestures, and AnimatePresence.

If you create custom animated components alongside SmoothUI, follow the same pattern:

src/components/FadeIn.tsx
"use client";

import { motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";

export type FadeInProps = {
  children: ReactNode;
};

const FadeIn = ({ children }: FadeInProps) => {
  const shouldReduceMotion = useReducedMotion();

  return (
    <motion.div
      initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={
        shouldReduceMotion
          ? { duration: 0 }
          : { type: "spring", duration: 0.25, bounce: 0.1 }
      }
    >
      {children}
    </motion.div>
  );
};

export default FadeIn;
src/pages/index.astro
---
import FadeIn from "../components/FadeIn";
---

<FadeIn client:visible>
  <p>This content fades in when scrolled into view.</p>
</FadeIn>

The 'use client' Directive

The "use client" directive at the top of React component files is a Next.js convention. Astro ignores it, so it does no harm — and it keeps your components compatible with both frameworks.

Troubleshooting

Next Steps