Server rendering
Using Youj with React Server Components and server rendering: what runs where, what the server and the first browser render agree on, and what changes after hydration.
Youj renders on the server. The packages mark their client components with "use client", so a Server Component imports them directly, with nothing to transpile, and the page arrives as HTML.
What runs where
- Import Youj components from a Server Component.
import { Card } from '@youj/react'works in a file with no directive. - Functions come from a Client Component. A prop that's a function (
onAction,onValueChange,render) can't cross from a Server Component. Put the component that passes it in a file that starts with'use client'. - A sound pack is such a function.
sound={beacon}goes on aYoujProviderin a client file;npx @youj/cli add soundwritesapp/providers.tsxfor this on the App Router. - Hooks are client-side.
useYouj,useDeviceand the rest read the provider, so use them in a Client Component. - What reads the clock goes inside Suspense under
cacheComponents. create-next-app turns Next.js'scacheComponentson, and thennext buildstops when a Client Component reads the current time while its page prerenders. The Date Range Picker does (its presets count from today), and so do blocks and examples that count from now (a countdown, an upload's progress, a schedule's current hour). Wrap them, or the part of the page around them, in<Suspense>and they render per request.npx @youj/cli createwraps its screens for you.
The theme, without a flash
Spread the theme onto <html> in your root layout:
import { basalt, themeProps } from '@youj/core'
<html lang="en" suppressHydrationWarning {...themeProps(basalt)}>themeProps returns the attributes that apply a theme to an element: its contract as CSS variables, the preset's id and its motion personality. The server's HTML already carries the theme, so the first paint is themed.
The light or dark scheme is set on <html> by a script before React hydrates, which is why <html> needs suppressHydrationWarning. npx @youj/cli init writes both in a Next.js or React Router app. See Installation for the setup by hand.
What the first render assumes
The server and the browser's first render must agree, so some things start from a fixed value and correct themselves right after hydration:
| What | On the server and the first render | After mount |
|---|---|---|
Device (useDevice) | pointer | touch if the primary pointer is coarse |
| Reduced motion | The OS setting is off, unless you set reducedMotion="always" on the provider | Follows the OS setting |
| Direction | The provider's or <html>'s dir, else ltr | auto is read from the element |
Quality (quality="auto") | The default tier | Measured on the device |
| Numbers, dates | The provider's locale, en-US by default | The same |
| "5 min ago", local clock times | UTC | The browser's time zone |
What this means for your code:
- Style with the
touch:variant, not withuseDevice. CSS applies before hydration. A device read in JS is for what a component does when it opens (a sheet or a popover), not for markup that's on the page at load. - Set what you know on the server. Pass the user's
localeanddirtoYoujProviderfrom the request, andreducedMotion="always"if you want it on from the first render. - Expect a relative time to change once. If your own markup shows one, render it the same way.
Motion
Motion's engine loads on demand, after the page: components render their resting state on the server and until the chunk lands, then animate. See Motion loads on demand.