Skip to content

React Server Components

Five components in tekivex-ui are RSC-safe: they call no hook, use no browser API, and attach no event handler, so they render on the server in Next.js App Router (or any RSC framework) without "use client" and ship no JavaScript to the browser.

ComponentNotes
TkxBadgeStatus pill
TkxDividerHorizontal / vertical separator
TkxEmptyEmpty-state placeholder
TkxIconIcon set
TkxSparklineInline trend chart (pure SVG)

Every other component calls useTheme(), which reads React context, or else uses state, refs, or event handlers. Context is not available in Server Components, so those have to stay client-side.

The five above escape it because of the CSS-variable change in 4.1.0: components paint with var(--tkx-token, fallback) rather than a resolved hex value, and ThemeProvider defines those variables on the DOM. A purely presentational component can therefore emit the variable reference directly — through the React-free cssTokens module — and let CSS resolve it. Theming, including live theme switching, keeps working with no JavaScript at all, because the resolution happens in CSS rather than in JS.

That is also the recipe for widening this list: remove a component’s dependency on context, rather than just removing its directive.

// app/dashboard/page.tsx — Server Component
import { TkxBadge, TkxDivider, TkxSparkline } from 'tekivex-ui';
export default async function Dashboard() {
const data = await fetch('https://api.example.com/metrics', {
cache: 'no-store',
}).then(r => r.json());
return (
<section>
<TkxBadge variant="success">{data.status}</TkxBadge>
<TkxDivider />
<TkxSparkline data={data.trend} />
</section>
);
}

No "use client", and no JavaScript shipped for those three. The HTML arrives fully rendered on first byte.

Anything interactive — buttons with onClick, forms with onChange, modals, the image editor, the captcha — must sit in a client boundary, as must any component that reads the theme through context (which today is everything not in the table above). The convention is to keep your 'use client' boundaries small and leaf-level:

// app/page.tsx — Server
import { TkxBadge } from 'tekivex-ui';
import { LoginForm } from './login-form';
export default function Page() {
return (
<section>
<TkxBadge variant="info">Beta</TkxBadge>
<LoginForm />
</section>
);
}
// app/login-form.tsx — Client island
'use client';
import { TkxInput, TkxButton } from 'tekivex-ui';
// …

The Server Component still ships the surrounding structure on first byte; only the form hydrates.