Server rendering
Render Onset on the server and defer clock, platform, and browser-dependent values until after hydration.
Design
Onset components can be rendered on the server. Components that read browser state defer those reads until hydration.
Components whose output varies by the reader’s machine (timestamps in the user’s timezone, keyboard shortcuts that depend on the OS) do not render live values on the server. They render a neutral, stable placeholder, match that on the first client render, then switch to live data once the client hydrates.
This avoids hydration mismatches: the HTML the server sends matches what React expects on first render. The switch happens in a subsequent render, driven by useHydrated().
The useHydrated pattern
useHydrated() returns false on the server and during the first client render, then true after hydration. Components that depend on browser-specific APIs or the reader’s machine use it to gate their live rendering:
function MyComponent() {
const hydrated = useHydrated();
if (!hydrated) return <NeutralVariant />; // server + first paint
return <LiveVariant />; // after hydration
}NeutralVariant supplies the same placeholder on the server and first client render. LiveVariant reads client-specific values after hydration.
useIsomorphicLayoutEffect
Any code that calls useLayoutEffect in a component that can render on the server must use useIsomorphicLayoutEffect instead. It falls back to useEffect on the server, where useLayoutEffect is not available and would emit a warning. Import it from the library:
import { useIsomorphicLayoutEffect } from "@solarius/onset";
useIsomorphicLayoutEffect(() => {
// Safe everywhere — no console warnings on the server.
measureElement(ref.current);
}, []);Affected components
The following components use the useHydrated() pattern. On the server they render a neutral, deterministic value; on the client they switch to a live, reader-specific value after hydration.
Timestamp / RelativeTime
These display a formatted date. The server renders the supplied instant as an absolute UTC value in a fixed locale. After hydration the component switches to the user’s timezone and locale, displaying the date in the reader’s local format. RelativeTime additionally computes the difference from “now” on the client.
import { Timestamp, RelativeTime } from "@solarius/onset";
// Server renders a neutral placeholder.
// Client renders in the user's timezone after hydration.
<Timestamp date={new Date("2025-07-20T14:00:00Z")} />
<RelativeTime date={new Date("2025-07-20T14:00:00Z")} />MiniCalendar
The server renders the calendar with a neutral month (month 0, year 2000). After hydration the client switches to the current month.
import { MiniCalendar } from "@solarius/onset";
// Server: neutral month.
// Client: current month.
<MiniCalendar />;ActivityFeed
The server renders every entry, grouping days in UTC and omitting reader-relative labels such as Today. After hydration it regroups in the reader’s timezone and enables relative labels.
import { ActivityFeed } from "@solarius/onset";
// Server: full feed grouped in UTC.
// Client: regrouped in the reader's timezone.
<ActivityFeed items={activityItems} />;KeyboardShortcut
The server renders the non-Mac form (e.g. Ctrl). After hydration the client determines the platform and renders the platform-specific key: ⌘ on macOS, Ctrl on Windows and Linux.
import { KeyboardShortcut } from "@solarius/onset";
// Server: renders Ctrl+K.
// Client: renders ⌘ or Ctrl depending on navigator.platform.
<KeyboardShortcut keys="mod+k" />;Framework compatibility
Onset components are React client components that use hooks and browser APIs. The built component entry starts with "use client"; the pure token entry does not.
Next.js Pages Router
Works with no configuration. Import the stylesheet and wrap with ThemeProvider in _app.tsx. The library components render on the server and hydrate on the client without any extra setup.
Next.js App Router
Supported without wrapper files or transpilePackages. Import the global styles and ThemeProvider from a root Server Component layout; Next recognizes the package boundary and prerenders the client component tree before hydration.
Other frameworks
Any React-based framework that handles CSS imports and supports hooks (Vite, Remix, Astro islands, or a plain Express + React setup) works. See Frameworks for per-framework setup instructions.