NEXT.JS
"use client" directive baked in, so you do not need to
wrap CathodeProvider or individual components to satisfy
React Server Components.
TL;DR
Cathode primitives are client components. They use
hooks (useState, useEffect), portals,
motion, and browser-only APIs (navigator.vibrate,
Web Audio), so they cannot execute on the server. The package
declares itself as a client boundary for you.
npm install @cathode-ui/react App Router setup
Put the CSS imports in your root layout.tsx. Wrap the
tree in CathodeProvider from a small client component
— the layout itself stays a Server Component (good for streaming
and metadata).
1. Client wrapper (app/providers.tsx):
'use client';
import type { ReactNode } from 'react';
import { CathodeProvider } from '@cathode-ui/react';
export function Providers({ children }: { children: ReactNode }) {
return <CathodeProvider>{children}</CathodeProvider>;
} 2. Root layout (app/layout.tsx):
import '@cathode-ui/react/tokens.css';
import '@cathode-ui/react/fonts.css';
import '@cathode-ui/react/styles.css';
import { Providers } from './providers';
export const metadata = { title: 'My app' };
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
} 3. Use primitives anywhere (Server or Client pages):
// app/page.tsx — can stay a Server Component.
import { Button, TerminalFrame, PulsingDot } from '@cathode-ui/react';
export default function Home() {
return (
<TerminalFrame title="STATUS">
<PulsingDot color="var(--cathode-color-success)" />
<Button variant="primary">SUBMIT</Button>
</TerminalFrame>
);
}
That works because every Cathode export is a client component — Next
crosses the client boundary at the @cathode-ui/react
import site, not at your page. You can render Cathode primitives
directly from Server Components without 'use client'
on the page file.
Pages Router setup
The Pages Router has no RSC boundary, so the setup is identical to a
standard React app. Mount the provider in pages/_app.tsx:
import type { AppProps } from 'next/app';
import '@cathode-ui/react/tokens.css';
import '@cathode-ui/react/fonts.css';
import '@cathode-ui/react/styles.css';
import { CathodeProvider } from '@cathode-ui/react';
export default function App({ Component, pageProps }: AppProps) {
return (
<CathodeProvider>
<Component {...pageProps} />
</CathodeProvider>
);
} Using with RSC
You can import a Cathode primitive inside a Server Component — Next's
compiler sees the "use client" directive on the Cathode
module and crosses the boundary automatically. The props you pass
must be serializable (strings, numbers, plain objects) because they
cross the server → client boundary.
Do not pass server-only values — functions from Server Actions are fine, but raw file handles, DB clients, etc. will fail serialization. This is a general RSC rule, not Cathode-specific.
Remix + React Router 7
Both frameworks adopt the same client-boundary model as Next App
Router. The root app/root.tsx stays a Server Component;
wrap the outlet in a client-side Providers component as
shown above. No Cathode-specific configuration required.
Troubleshooting
Error: "You're importing a component that needs useState / useEffect …"
If you see this when using Cathode inside an App Router page,
upgrade @cathode-ui/react to 0.4.0 or
later — earlier versions did not ship the "use client"
directive in the bundle.
npm install @cathode-ui/react@latest Error: "Functions cannot be passed directly to Client Components"
You're passing a non-serializable prop (a closure, class instance, or Date/Map/Set) from a Server Component into a Cathode primitive. Move the prop into a local client component, or serialize it before the boundary.
Fonts not loading
@cathode-ui/react/fonts.css ships JetBrains Mono via
@font-face — Next will tree-shake it if you don't
import it from a client module. Keep the import in
app/layout.tsx (as shown above) so it lands in the
global CSS stream.
See also: Getting Started, all components.