Next.js Setup
Primus UI works with both the Next.js App Router (Next.js 13+) and Pages Router. Because Primus components use React hooks and browser APIs, they must run as Client Components.
Install​
npm install primus-react-ui @primus/ui-core
Import styles​
In your root layout (App Router) or _app.tsx (Pages Router):
- App Router
- Pages Router
// app/layout.tsx
import '@primus/ui-core/dist/primus-ui.min.css';
import 'primus-react-ui/styles.css';
import { PrimusThemeProvider, PrimusUiCoreBridge } from 'primus-react-ui';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<PrimusThemeProvider defaultTheme="dark">
<PrimusUiCoreBridge />
{children}
</PrimusThemeProvider>
</body>
</html>
);
}
// pages/_app.tsx
import '@primus/ui-core/dist/primus-ui.min.css';
import 'primus-react-ui/styles.css';
import { PrimusThemeProvider, PrimusUiCoreBridge } from 'primus-react-ui';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<PrimusThemeProvider defaultTheme="dark">
<PrimusUiCoreBridge />
<Component {...pageProps} />
</PrimusThemeProvider>
);
}
Mark components as Client Components​
Primus SDK components use hooks internally. Any file that imports from primus-react-ui must have 'use client' at the top.
// app/dashboard/page.tsx — Server Component (fine, no Primus imports)
import { DashboardClient } from './dashboard-client';
export default function DashboardPage() {
return <DashboardClient />;
}
// app/dashboard/dashboard-client.tsx — Client Component
'use client';
import { PrimusStatCard, PrimusDataTable } from 'primus-react-ui';
import { useState, useEffect } from 'react';
export function DashboardClient() {
const [stats, setStats] = useState(null);
useEffect(() => {
fetch('/api/stats').then(r => r.json()).then(setStats);
}, []);
return (
<div>
<PrimusStatCard title="Revenue" value={stats?.revenue ?? '—'} />
<PrimusDataTable columns={cols} data={stats?.rows ?? []} rowKey="id" />
</div>
);
}
Rule of thumb
Create a *-client.tsx wrapper for any page that uses Primus components. Keep data fetching in the Server Component above it.
next.config.js — transpilePackages​
If you see module resolution errors, add primus-react-ui to transpilePackages:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['primus-react-ui'],
};
module.exports = nextConfig;
Auth with Next.js​
Use PrimusProvider in the root layout for auth wiring:
// app/layout.tsx
'use client';
import { PrimusProvider, PrimusThemeProvider, PrimusUiCoreBridge } from 'primus-react-ui';
import '@primus/ui-core/dist/primus-ui.min.css';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<PrimusProvider
authority="https://api.yourdomain.com"
authBasePath="/api/auth"
clientId="my-app">
<PrimusThemeProvider defaultTheme="dark">
<PrimusUiCoreBridge />
{children}
</PrimusThemeProvider>
</PrimusProvider>
</body>
</html>
);
}
Protecting routes (App Router)​
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(req: NextRequest) {
const token = req.cookies.get('primus_token')?.value;
if (!token && req.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', req.url));
}
}
export const config = { matcher: ['/dashboard/:path*'] };
Common issues​
| Error | Fix |
|---|---|
window is not defined | Add 'use client' to the file importing from primus-react-ui |
useContext / hook errors in Server Component | Same fix — add 'use client' |
| CSS not loading in production | Ensure primus-react-ui is in transpilePackages in next.config.js |
| Flash of unstyled content | Import CSS in layout.tsx before any component rendering |