Next.js + Untitled UI
Untitled UI integrates seamlessly with Next.js applications, supporting both the App Router and Pages Router architectures. This guide will help you set up a Next.js project with Untitled UI, configure the necessary providers, and implement client-side routing.
Installation
The easiest way to get started with Untitled UI in a Next.js project is to use our CLI:
Initialize a new Next.js project with Untitled UI
Using our CLI, you can initialize a new Next.js project with Untitled UI with the following command:
npx untitledui@latest init untitled-ui
While running the command, you'll be asked a few questions to set up your project:
? What is your project named? › untitled-ui ? Which color would you like to use as the brand color? › ❯ brand error warning success ↓ gray-neutral
This will create a new Next.js project in the untitled-ui
directory with all the necessary configurations and components pre-installed.
Ready to go!
Great! You're all set to start using Untitled UI components.
If something is missing, just head over to the component's page and copy/paste what you need into your project.
Manual installation
If you prefer to add Untitled UI to an existing Next.js project, you can follow these steps:
Install packages
Simply install the required packages with your favorite package manager:
bun add @untitledui/icons react-aria-components tailwindcss @tailwindcss/postcss postcss tailwindcss-react-aria-components tailwind-merge tailwindcss-animate next-themes
Set up CSS
In your global CSS file (usually globals.css
), import Tailwind CSS and our plugins directly:
@import "tailwindcss"; @import "tailwindcss-react-aria-components"; @import "tailwindcss-animate"; @import "./theme.css";
This is one of the biggest changes in Tailwind CSS v4.1 - you no longer need a separate configuration file and instead import plugins directly in your CSS.
Setting up providers
To ensure Untitled UI components work correctly with Next.js, you need to set up a few providers:
Route provider
For client-side routing integration with React Aria components, create a RouteProvider
component:
// app/_components/providers/route-provider.tsx "use client"; import type { PropsWithChildren } from "react"; import { useRouter } from "next/navigation"; import { RouterProvider } from "react-aria-components"; declare module "react-aria-components" { interface RouterConfig { routerOptions: NonNullable<Parameters<ReturnType<typeof useRouter>["push"]>[1]>; } } export const RouteProvider = ({ children }: PropsWithChildren) => { let router = useRouter(); return <RouterProvider navigate={router.push}>{children}</RouterProvider>; };
Theme provider
For dark mode support, use the next-themes
package:
// app/_components/providers/theme-provider.tsx "use client"; import { ThemeProvider as NextThemeProvider } from "next-themes"; import { type PropsWithChildren } from "react"; export function ThemeProvider({ children }: PropsWithChildren) { return ( <NextThemeProvider attribute="class" value={{ light: "light-mode", dark: "dark-mode" }}> {children} </NextThemeProvider> ); }
Root layout
Add the providers to your root layout:
// app/layout.tsx import type { Metadata, Viewport } from "next"; import { Inter } from "next/font/google"; import { RouteProvider } from "@/app/_components/providers/route-provider"; import { ThemeProvider } from "@/app/_components/providers/theme-provider"; import "@/styles/globals.css"; const inter = Inter({ subsets: ["latin"], display: "swap", variable: "--font-inter", }); export const metadata: Metadata = { export const title = "Your App Name", export const description = "Your app description", }; export const viewport: Viewport = { colorScheme: "light", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en" suppressHydrationWarning className={inter.variable}> <body className="light-mode scroll-smooth bg-primary antialiased"> <RouteProvider> <ThemeProvider> {children} </ThemeProvider> </RouteProvider> </body> </html> ); }
App router vs pages router
Untitled UI supports both Next.js routing architectures. Here's how to set up each:
App router
The setup shown above is for the App Router. If you're using the App Router, make sure your client-side components that use interactivity have the "use client"
directive.
Pages router
If you're using the Pages Router, create a custom _app.tsx
file:
// pages/_app.tsx import type { AppProps } from "next/app"; import { type NextRouter, useRouter } from "next/router"; import { RouterProvider } from "react-aria-components"; import { ThemeProvider } from "next-themes"; import "@/styles/globals.css"; declare module "react-aria-components" { interface RouterConfig { routerOptions: NonNullable<Parameters<NextRouter["push"]>[2]>; } } export default function MyApp({ Component, pageProps }: AppProps) { const router = useRouter(); return ( <RouterProvider navigate={(href, opts) => router.push(href, undefined, opts)} > <ThemeProvider attribute="class" value={{ light: "light-mode", dark: "dark-mode" }}> <Component {...pageProps} /> </ThemeProvider> </RouterProvider> ); }
Using Untitled UI components
After setting up the providers, you can use Untitled UI components throughout your application:
import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; export default function Home() { return ( <main className="container mx-auto p-4"> <h1 className="text-2xl font-bold">Welcome to Untitled UI + Next.js</h1> <div className="mt-4"> <Input placeholder="Enter your name" /> <Button className="mt-2">Submit</Button> </div> </main> ); }
Dark mode
Untitled UI supports dark mode out of the box. The implementation uses CSS variables to provide a seamless experience.
Theme toggle
Create a toggle to switch between light and dark mode:
"use client"; import { useTheme } from "next-themes"; import { Button } from "@/components/ui/button"; import { MoonIcon, SunIcon } from "@untitledui/icons"; export function ThemeToggle() { const { theme, setTheme } = useTheme(); return ( <Button variant="ghost" size="icon" onClick={() => setTheme(theme === "light-mode" ? "dark-mode" : "light-mode")} > {theme === "light-mode" ? <MoonIcon /> : <SunIcon />} <span className="sr-only">Toggle theme</span> </Button> ); }
FAQs
To integrate Untitled UI with Next.js, you can either use our CLI command (npx untitledui@latest init) for a complete setup, or manually install the required packages and import Tailwind CSS along with our plugins directly in your CSS file. With Tailwind CSS v4.1, there's no need for a separate configuration file - everything is imported directly in your CSS.
Yes, Untitled UI supports both the App Router and Pages Router in Next.js. We provide specific setup instructions for each approach, including how to configure the RouterProvider component to enable client-side navigation with React Aria components.
Yes, many Untitled UI components work with server components. However, interactive components that use React Aria hooks need to be client components. Simply add the 'use client' directive at the top of files containing interactive components. Our documentation clearly identifies which components require client-side rendering.
Untitled UI includes built-in dark mode support using CSS variables. In Next.js, we recommend using the next-themes package (included in our installation instructions) to manage theme switching. Our ThemeProvider setup uses the 'light-mode' and 'dark-mode' classes to toggle between themes, with no additional configuration needed.
Untitled UI is fully compatible with Tailwind CSS v4.1. The new approach in v4.1 is much simpler - you just import Tailwind CSS and any plugins directly in your CSS file (like globals.css) rather than using a tailwind.config.js file. Our theme styles are imported the same way, making setup easier than with previous Tailwind versions.
Yes, Untitled UI components can be used with Next.js's Image component. For components that display images (like avatars or cards), you can nest the Next.js Image component inside our UI components to take advantage of Next.js's automatic image optimization, lazy loading, and responsive sizing.