UI Package
@rising-tide/ui - React hooks, auth providers, Privy integration, app shell, and design tokens for building on the protocol.
@rising-tide/ui is the React layer for Rising Tide Protocol. The SDK is headless - it ships data and actions with no opinion about rendering. @rising-tide/ui is where the React surface lives: Query provider and data hooks, auth providers, the Privy wallet integration, the app shell, and the design tokens.
The package depends on @rising-tide/sdk. You bring both, and @rising-tide/ui re-exposes the SDK's data through hooks while adding the provider wiring an app needs.
Current version
The newest entry in the Changelog is the current version - a number printed here would drift the moment the next tarball ships. Distributed as a tarball alongside the SDK (rising-tide-ui-<version>.tgz). The two move in lockstep: @rising-tide/ui imports types from @rising-tide/sdk, so bump and install both together.
Entry points
| Entry point | What it carries |
|---|---|
@rising-tide/ui/providers | RisingTideProviders - the full provider stack in one config-driven component, including the entry curtain every app gets |
@rising-tide/ui/react | React Query provider and data hooks |
@rising-tide/ui/auth | Auth providers, AuthGate, ProtocolUserProvider, sign-in UI |
@rising-tide/ui/shell | App shell primitives (AppShell, SubNav, FootBar, TestnetBanner), the data-driven UserAppRail, and AccessGate |
@rising-tide/ui/tokens | Design tokens (foundations.css) - the raw --rt-* layer |
@rising-tide/ui/theme | Semantic @theme mapping and brand element defaults (import after tokens) |
@rising-tide/ui/primitives | Brand-density shadcn primitives: Button, Card and its slots, Badge, Input, Spinner, LoadingState, EmptyState, ErrorState, Toaster / ToastProvider / useToast, plus cn |
@rising-tide/ui/storage | The owner's Save As surface: the chip, the automatic hook, the posture hooks and the signers |
@rising-tide/ui/testing | Test scaffolding, never product code: checkButtonContract for an app that forks a Rising Tide primitive, and MockProtocolUserProvider / MOCK_PROTOCOL_USER to render components that read useProtocolUser without Privy |
@rising-tide/ui/shell.css, @rising-tide/ui/safe-area.css | Stylesheets. shell.css styles the shell components; safe-area.css is the shared safe-area utilities, which only work once viewportFit: "cover" is set in the root layout |
The package root exports nothing but a version constant: every surface is addressed by its subpath.
Hooks on the entry points with no page of their own
shell, primitives and storage have no reference page yet. Nine hooks ship from them and are
in real use across the estate, so they are named here rather than left to be discovered:
| Hook | Entry point | What it answers |
|---|---|---|
useToast() | primitives | The toast queue. Pair it with the Toaster the same entry point ships |
useUserApps() | shell | The signed-in user's apps, as the rail renders them - each with an OPEN / UNLOCKED / LOCKED state |
useAppAccess() | shell | This app's room state for the current viewer - OPEN, UNLOCKED, LOCKED or UNKNOWN - from GET /api/apps/{id}/access. The entry curtain reads it for you |
useProtocolHealth() | shell | Substrate health, for an operator surface |
useStoragePosture() | storage | What the signed-in owner's storage key can hold - allowance, credits, whether room can be bought |
useEnableStorage() | storage | Creates the owner's storage key in their session and records it |
usePrivyStorageSigner() | storage | The signer a custodial session saves with |
useStorageTopUp() | storage | The card top-up, including the poll that waits for bought room to land |
useAutoSaveAs() | storage | Runs Save As for the owner on eligible editions they own, once per edition per throttle window |
useUserApps is the rail-as-catalog read, and the state matters: LOCKED is an app the user has
not opened yet rather than one that failed, and a teased locked app is a door on the ladder rather
than an error. Access is granted by an invite or earned by climbing - see
Achievements.
The five storage hooks pair with SaveAsButton, which the same entry point ships. The button
is the surface, not the hooks - an app that wants the standard behaviour mounts it and passes
onNeedsRoom only if it has somewhere to send an owner who is out of room. Leaving that prop off
is what turns a refusal into a plain statement instead of an offer nobody can honour. See
Storage.
useAppAccess is what the entry curtain resolves from, and an app does not need to call it to
get a locked screen - RisingTideProviders renders AccessGate for every app. Call it yourself
only for a second surface that wants the same verdict (a button that should not render in a locked
room, for instance). It is never the gate: the acquisition and participation routes enforce access
server-side and fail closed, so a client that skipped it gets a 403 rather than an unguarded write.
See Providers for what the curtain does and how to opt out.
In this section
- Providers -
RisingTideProviders, the one-line provider setup - React - React Query provider and hooks
- Auth - Auth providers, AuthGate, user identity
- Privy - how Privy is wired, and where each piece comes from
Start here
Most apps wire everything through RisingTideProviders, which encodes the correct provider order once. Reach for the individual react and auth entry points only when you need to assemble the stack by hand.