Next.js

Learn how to use SmoothUI animated React components in Next.js projects with App Router, Tailwind CSS 4, and Motion.

Last updated: March 29, 2026

Overview

Next.js is the recommended framework for SmoothUI. With App Router, React Server Components, and built-in Tailwind CSS support, Next.js provides the best developer experience for building animated interfaces. SmoothUI components work seamlessly with both the App Router and Pages Router.

How It Works

SmoothUI components are React client components that use the "use client" directive. In Next.js App Router, they render on the server and hydrate on the client — animations start smoothly after hydration with no extra configuration.

Prerequisites

Before getting started, make sure you have the following:

RequirementMinimum VersionPurpose
Node.js18+Runtime
Next.js15.0+Framework
React19.0+UI library
Tailwind CSS4.0+Styling
Motion12.0+Animations (Framer Motion)

Project Setup

Create a New Next.js Project

If you're starting from scratch, create a new Next.js project with TypeScript and Tailwind CSS:

npx create-next-app@latest my-smoothui-app --typescript --tailwind --app
cd my-smoothui-app

This scaffolds a Next.js project with App Router, TypeScript, and Tailwind CSS 4 pre-configured.

Tailwind CSS 4 Configuration

If you used create-next-app with the --tailwind flag, Tailwind CSS 4 is already configured. Your app/globals.css should contain:

app/globals.css
@import "tailwindcss";

If you're adding Tailwind to an existing project, install it:

pnpm add tailwindcss @tailwindcss/postcss

Then configure PostCSS:

postcss.config.mjs
const config = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

export default config;

Install Motion

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

pnpm add motion

Initialize shadcn/ui

SmoothUI builds on the shadcn ecosystem. Initialize shadcn in your project:

pnpm dlx shadcn@latest init

Follow the prompts to configure your project. This sets up components.json and the cn() utility that SmoothUI components depend on.

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

No Extra Configuration

SmoothUI is an official shadcn registry. You don't need to add anything to components.json — just use the @smoothui namespace and components are installed automatically.

Server Component Boundaries

Next.js App Router uses React Server Components by default. SmoothUI components include the "use client" directive, so they automatically become client components when imported. Here's what you need to know:

PatternBehavior
Import SmoothUI component in a Server ComponentWorks — the component boundary switches to client at the import.
Import SmoothUI component in a Client ComponentWorks — standard client-side rendering.
Pass Server Component children to SmoothUIWorks — Server Components can be passed as children to client components.
Use SmoothUI in layout.tsxWorks — layout renders on server, SmoothUI component hydrates on client.

Example: Server Component with SmoothUI

app/page.tsx
import { SiriOrb } from "@/components/smoothui/ui/SiriOrb";
import { MagneticButton } from "@/components/smoothui/ui/MagneticButton";

// This is a Server Component — SmoothUI components hydrate on the client
export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center gap-8 p-8">
      <h1 className="text-4xl font-bold">SmoothUI + Next.js</h1>
      <SiriOrb size="200px" />
      <MagneticButton>Get Started</MagneticButton>
    </main>
  );
}

Example: Passing Server Component Children

app/page.tsx
import { ExpandableCards } from "@/components/smoothui/ui/ExpandableCards";

// Server-fetched data passed to a client component
export default async function Page() {
  const items = await fetchItems(); // Runs on the server

  return (
    <ExpandableCards items={items} />
  );
}

No 'use client' Needed in Your Pages

You do not need to add "use client" to your page or layout files just because they use SmoothUI components. The directive is already in the component files themselves. Your pages stay as Server Components, keeping data fetching on the server.

App Router vs Pages Router

SmoothUI works with both routing systems, but there are differences to be aware of:

The App Router is the recommended approach. Server Components are the default, and SmoothUI's "use client" directive handles the boundary automatically.

app/dashboard/page.tsx
import { AnimatedTabs } from "@/components/smoothui/ui/AnimatedTabs";

export default function Dashboard() {
  return <AnimatedTabs tabs={["Overview", "Analytics", "Settings"]} />;
}

Pages Router

In the Pages Router, every page is a client component by default, so there are no Server Component boundaries to consider. SmoothUI components work the same way as any other React component:

pages/dashboard.tsx
import { AnimatedTabs } from "@/components/smoothui/ui/AnimatedTabs";

export default function Dashboard() {
  return <AnimatedTabs tabs={["Overview", "Analytics", "Settings"]} />;
}

SSR in Pages Router

Pages Router still uses SSR via getServerSideProps or SSG via getStaticProps. SmoothUI components render on the server and hydrate on the client — the same as App Router, just without the Server Component model.

Custom Animated Components

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

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;

Troubleshooting

Next Steps