Farther ShoreDocs
Go to Farther Shore
What is FartherShore
Install the CLI
Quickstart
Core concepts
The @Product class
Meters & resources
Features & routes
Capabilities & entitlements
Plans & pricing
The Manifest IR
Bring your own backend
Transport modes
Metering & verification
Runtime tokens
Frontend SDK
Root & data components
The data componentsAuth-gated contentRendering a product-authored nav after a forkNext
Auth & sessions
Entitlement gates
Connect Stripe
Subscriptions & usage
Plan changes & grandfathering
Billing strategies
Apply & deploy
Environments
Migrations
Docs versions & archive
Operate with an agent
Operation classes
MCP server
End-to-end via CLI/MCP
CLI reference
@farthershore/product
@farthershore/backend
@farthershore/farthershore-js
Environment variables
Response & deny codes
Add a metered capability
Gate a feature
Change a price
Prepaid credits
Meter AI tokens
Operate via an agent
Prepare for launch
Status
Docs/Build the frontend/Root & data components

Root & data components

FartherShoreRoot and the self-managed, zero-prop data components.

PreviousFrontend SDKNextAuth & sessions

The component kit gives you a portal with almost no code. <FartherShoreRoot> is the single mount point — provider, bootstrap gate, theming, and managed auth in one wrapper — and under it every data component is self-managed: it works with zero props, fetching its own data through the SDK hooks. Props are optional overrides. Drop in <PlansTable/>, <UsageCard/>, <ApiKeysPanel/>, <BillingSummary/> and you have a working dashboard.

Components import from the /components subpath; load the stylesheet once.

import {
  FartherShoreRoot,
  PlansTable,
  UsageCard,
  ApiKeysPanel,
  BillingSummary,
} from "@farthershore/farthershore-js/components";
import "@farthershore/farthershore-js/components/styles.css";

<FartherShoreRoot>

One wrapper gives a host app everything self-managed. It owns the client provider, the bootstrap gate (one cached resolve round-trip, with splash + error states), the theme root (the brand color from the product's branding), and the managed auth layer (Clerk satellite or persona — picked from the environment automatically). No host glue.

Everything inside <FartherShoreRoot> renders only after the product resolves, so child components and useBoot() never see a null product.

import { createFartherShoreClient } from "@farthershore/farthershore-js";
import {
  FartherShoreRoot,
  PlansTable,
  UsageCard,
  ApiKeysPanel,
  BillingSummary,
} from "@farthershore/farthershore-js/components";
import "@farthershore/farthershore-js/components/styles.css";

const fs = createFartherShoreClient.fromEnv({
  coreUrl: import.meta.env.VITE_FS_CORE_URL,
});

export default function CronCloudPortal() {
  return (
    <FartherShoreRoot client={fs}>
      <main className="fs-stack">
        <PlansTable />
        <UsageCard />
        <BillingSummary />
        <ApiKeysPanel />
      </main>
    </FartherShoreRoot>
  );
}

That is a complete, working portal for the CronCloud product — plans, live usage, subscription state, and API-key management, all driven by the product's contract.

Props

<FartherShoreRoot
  client={fs}
  appearance={{ /* theme overrides; brand color applies automatically */ }}
  clerk={{ publishableKey: "pk_test_…" }} // clerk-strategy envs (public value)
  splash={<MySplash />}                   // while the product resolves
  renderError={(error, retry) => <Failed error={error} onRetry={retry} />}
  renderCrash={(error, reset) => <Crashed error={error} onReset={reset} />}
  envBadge={true}                         // auto "Test mode" badge; opt-out only
>
  {/* … */}
</FartherShoreRoot>
  • appearance overrides the theme; the product's branding.primaryColor is applied as the brand token automatically (explicit wins).
  • clerk supplies the Clerk connection on clerk-strategy environments. Portals served through the edge get it from the injected config, so you usually omit it. See Auth & sessions.
  • renderError handles a resolve failure (bootstrap fetch failed); retry re-runs the resolve without a full reload. renderCrash handles a render throw in the gated subtree; reset clears the boundary.
  • envBadge mounts the automatic "Test mode" indicator on preview/test environments — opt out with false, never opt in. It renders nothing on production.

<FartherShoreRoot> wraps <FartherShoreProvider> and adds the gate. If you only need the client in the tree (no gate/theme/auth), use the bare <FartherShoreProvider> instead.

useBoot()

Inside the root, useBoot() returns the resolved Bootstrap (product, branding, environment, plans). It never suspends or returns null because children render only after resolve.

import { useBoot } from "@farthershore/farthershore-js/components";

function Header() {
  const boot = useBoot();
  return <h1>{boot.branding.displayName}</h1>;
}

The data components

Each owns one Core endpoint and renders the matching surface with zero props. They accept className and expose a typed <Name>Props interface for wrapping. Mount only the ones you want.

ComponentRenders
<PlansTable/>The product's plan catalog with prices, grants, and a subscribe/checkout CTA.
<UsageCard/>Live metered usage for the period (used / included per meter).
<ApiKeysPanel/>The subscriber's API keys with create / revoke / rotate.
<BillingSummary/>Subscription status, plan, renewal, and a billing-portal link.
<CreditBalance/>The prepaid/credit balance for the current subscription.
<FeaturePanel/>A Gateway feature-invoke box (set a key, call a route).
<UpgradePrompt/>Upsell CTA — compose with a LimitExceededError / FeatureNotGrantedError.
// Zero-prop is the norm; the only props are optional overrides.
<UsageCard />
<UsageCard className="my-card" />
<PlansTable />
<ApiKeysPanel />
<BillingSummary />

<UsageCard/> renders a row per metered dimension the subscriber's plan defines — for CronCloud that's the requests meter its @Plan limits enforce (600/min on Starter). Its only props are className and activeCompiledPlanId (override the plan it reads from). For the "N of M" capability view (e.g. "7 of 10 cron jobs used"), mount <CapabilityUsageCard/> instead. See usage & metering and plans.

Conventions (hold for every component)

  • Self-managed: zero props under <FartherShoreRoot>; data via the SDK hooks. Props are optional overrides.
  • className is appended to the root; every component exports <Name>Props.
  • Naming: domain components are unprefixed (PlansTable, UsageCard); auth/chrome primitives carry the Fs prefix (FsSignIn, FsUserButton) to avoid collisions.
  • Styling: plain CSS over --fs-* tokens in styles.css (import once). Rules live in @layer farthershore so your host CSS wins without specificity wars.
  • Independence: components never depend on a host router or layout.

Auth-gated content

To reveal a subtree only to signed-in users, wrap it in RequireAuth. To gate by plan capability or feature, use <FeatureGate> / <RequireCapability>. Both are managed and self-fed under <FartherShoreRoot>.

import { RequireAuth } from "@farthershore/farthershore-js/components";

<RequireAuth fallback={<p>Sign in to manage keys.</p>}>
  <ApiKeysPanel />
</RequireAuth>

Rendering a product-authored nav after a fork

A @Product can author a @Frontend({ nav, pages }) manifest (CronCloud declares a /cron nav entry gated on the managed-cron capability). The managed template renders it, but a fork that replaces the template silently drops it. <FsManifestNav> is the opt-in fix — it maps the manifest's nav entries to links so the authored nav survives a fork with one import. Zero-prop, accessible, and renders nothing when no manifest was authored.

import { FsManifestNav } from "@farthershore/farthershore-js/components";

// Renders boot.frontendManifest.nav; swap in a router <Link> via linkComponent.
<FsManifestNav activePath={location.pathname} />;

Next

  • Auth & sessions — sign-in, sessions, and RequireAuth.
  • Entitlement gates — <FeatureGate> / <RequireCapability>.