How to install and set up OUI in a Next.js project
This guide will help you set up OUI in a new or existing Next.js project with Tailwind CSS v4.
OUI has been tested with the following dependency versions:
If you're starting from scratch, create a new Next.js project (or use Starter Kit):
pnpm create next-app@latest my-app --typescript --eslint
cd my-app
Install OUI packages and peer dependencies:
pnpm add @opengovsg/oui @opengovsg/oui-theme react-aria-components motion
Install Tailwind CSS v4 and the PostCSS plugin:
pnpm add tailwindcss @tailwindcss/postcss
Create or update your postcss.config.mjs file to use the Tailwind CSS plugin:
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
}
export default configCreate or update your global CSS file (e.g., app/globals.css) to import the OUI theme:
@import "@opengovsg/oui-theme/tailwind.css";
/*
* Note: You may need to change the path relative to this css file to fit your
* project structure, especially in a monorepo.
*/
@source "../path/to/node_modules/@opengovsg/oui-theme";This import includes:
tailwindcss-react-aria-componentstw-animate-cssThere are a few ways to set up the Inter font in your Next.js project:
next/fontOne way is to use the next/font package:
import { Inter } from "next/font/google"
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
})Then, add the font variable to your HTML or body element:
<html lang="en" className={inter.variable}>
<body>{children}</body>
</html>You will also need to ensure that your Tailwind configuration uses the Inter font for the --font-sans CSS variable:
@import "@opengovsg/oui-theme/tailwind.css";
@source "../path/to/node_modules/@opengovsg/oui-theme";
@theme {
--font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
}Alternatively, you can use the inter-ui NPM package.
Install the package:
pnpm add inter-ui
Then, import the font CSS in your global CSS file:
@import "@opengovsg/oui-theme/tailwind.css";
@source "../path/to/node_modules/@opengovsg/oui-theme";
@import "inter-ui/inter.css";
@import "inter-ui/inter-variable.css";
@layer base {
:root {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
@supports (font-variation-settings: normal) {
--font-sans: "InterVariable", ui-sans-serif, system-ui, sans-serif;
}
}
}Make sure your CSS file is imported in your root layout:
import type { Metadata } from "next"
import "./globals.css"
export const metadata: Metadata = {
title: "My App",
description: "My app description",
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}react-aria-component Link providerIf you want proper client-side navigation when using react-aria-component's Link component with Next.js's router,
wrap your app with the RouterProvider:
"use client"
import { useRouter } from "next/navigation"
import { RouterProvider } from "react-aria-components"
export function Providers({ children }: { children: React.ReactNode }) {
const router = useRouter()
return <RouterProvider navigate={router.push}>{children}</RouterProvider>
}Then use it in your layout:
import type { Metadata } from "next"
import { Providers } from "./providers"
import "./globals.css"
export const metadata: Metadata = {
title: "My App",
description: "My app description",
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}Read more about Client Side Routing with react-aria-components here.
You're all set! Start using OUI components in your app.
Note: Next.js's
app/directory uses Server Components by default. OUI components can be imported directly in Server Components since components have theuse clientdirective added.However, if errors occur, add the
"use client"directive at the top of files that use OUI components, or wrap them in a Client Component.
"use client"
import { Button } from "@opengovsg/oui"
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center gap-4">
<Button>Default</Button>
<Button color="main">Main</Button>
<Button variant="outline">Outline</Button>
</div>
)
}The examples above use the App Router (Next.js 13+). If you're using the Pages Router, the setup is similar:
pages/_app.tsx instead of app/layout.tsxpages/_app.tsximport type { AppProps } from "next/app"
import { useRouter } from "next/router"
import { RouterProvider } from "react-aria-components"
import "../styles/globals.css"
export default function App({ Component, pageProps }: AppProps) {
const router = useRouter()
return (
<RouterProvider navigate={router.push}>
<Component {...pageProps} />
</RouterProvider>
)
}For the best TypeScript experience, ensure your tsconfig.json includes:
{
"compilerOptions": {
"moduleResolution": "bundler",
"jsx": "preserve"
}
}