Next.js 16 • React 19 • BCL

Capability-first progressive enhancement.

Ship a baseline-first page, then selectively upgrade visuals and interactivity when the device (and user preferences) say it is safe.

Tierrich
motionsmoothScrollrichImagesaudioprivacyBanner
Motion visualGated by the `motion` feature (respects reduced motion).

1) Render by tier with CakeLayer

Each layer has a built-in baseline fallback. Try overriding tiers via the BCL DevTools (top-right) to see the swaps.

Base (always)

Readable layout, crisp typography, no assumptions.

Lite layer

Now we can safely opt into richer imagery and minor UX polish.

Rich layer

Eligible for client-side upgrades and premium interactions.

Ultra layer

Locked until the tier reaches ultra.

import { CakeLayer } from "@shiftbloom-studio/birthday-cake-loading";

<CakeLayer minTier="rich" fallback={<HeroStatic />}>
  <HeroAnimated />
</CakeLayer>

2) Lazy-load enhancements with CakeUpgrade

Use strategies like idle, visible, and interaction so upgrades load only when needed.

Idle upgradestrategy: idle • gate: motion

Waits for idle time before loading. Great for non-critical polish.

Visible upgradestrategy: visible • gate: rich+

Loads only once the card scrolls near the viewport.

Interaction upgradestrategy: interaction • gate: rich+

Hover, focus, or click to trigger the import.

Ultra celebrationstrategy: timeout • gate: ultra

A small delight reserved for the highest tier.

import { CakeUpgrade } from "@shiftbloom-studio/birthday-cake-loading/upgrade";

<CakeUpgrade
  strategy={{ type: "visible", rootMargin: "300px" }}
  minTier="rich"
  loader={() => import("./RichGallery")}
  fallback={<GalleryStatic />}
/>

3) Signals and feature gates

BCL derives features from capability + preferences. For example: motion is off when reduced-motion is on.

SignalValue
saveData
prefersReducedData
prefersReducedMotion
effectiveType
downlinkMbps
rttMs
deviceMemoryGB
hardwareConcurrency
devicePixelRatio
screenWidth
screenHeight
userAgentMobile

4) Feature-first gates

Sometimes you want to gate on a concrete feature (like richImages or privacyBanner) instead of thinking in tiers. BCL lets you do that.

Feature gate: richImagesenabled

Rich imagery is allowed. This is where you can increase image quality, enable richer art-direction, or swap to heavier components.

Feature gate: privacyBannerenabled

Render a richer consent UI only when BCL says it is safe (rich+ and not Save-Data).

(Demo UI — wire this up to your real consent system.)

5) Server bootstrap (SSR)

On the server, BCL can precompute signals and a tier from request headers (Client Hints). This keeps SSR and hydration consistent and avoids guessing.

Computed on the server

Tier: rich • Features: motion=on • smoothScroll=on • audio=on • privacyBanner=on • richImages=on

View request headers used
Save-Data: —
ECT: —
Downlink: —
RTT: —
Device-Memory: —
DPR: —
Viewport-Width: —
Viewport-Height: —
Sec-CH-UA-Mobile: —
Sec-CH-Prefers-Reduced-Motion: —
Sec-CH-Prefers-Reduced-Data: —

Bootstrap JSON

The exact object you can pass to CakeProvider as bootstrap.

{
  "signals": {},
  "tier": "rich"
}
// app/layout.tsx (Next.js App Router)
import { headers } from "next/headers";
import { getServerCakeBootstrapFromHeaders } from "@shiftbloom-studio/birthday-cake-loading/server";

export default async function RootLayout({ children }) {
  const bootstrap = getServerCakeBootstrapFromHeaders(await headers());
  return <Providers bootstrap={bootstrap}>{children}</Providers>;
}