Rising Tide Developers
UI Package

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 pointWhat it carries
@rising-tide/ui/providersRisingTideProviders - the full provider stack in one config-driven component, including the entry curtain every app gets
@rising-tide/ui/reactReact Query provider and data hooks
@rising-tide/ui/authAuth providers, AuthGate, ProtocolUserProvider, sign-in UI
@rising-tide/ui/shellApp shell primitives (AppShell, SubNav, FootBar, TestnetBanner), the data-driven UserAppRail, and AccessGate
@rising-tide/ui/tokensDesign tokens (foundations.css) - the raw --rt-* layer
@rising-tide/ui/themeSemantic @theme mapping and brand element defaults (import after tokens)
@rising-tide/ui/primitivesBrand-density shadcn primitives: Button, Card and its slots, Badge, Input, Spinner, LoadingState, EmptyState, ErrorState, Toaster / ToastProvider / useToast, plus cn
@rising-tide/ui/storageThe owner's Save As surface: the chip, the automatic hook, the posture hooks and the signers
@rising-tide/ui/testingTest 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.cssStylesheets. 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:

HookEntry pointWhat it answers
useToast()primitivesThe toast queue. Pair it with the Toaster the same entry point ships
useUserApps()shellThe signed-in user's apps, as the rail renders them - each with an OPEN / UNLOCKED / LOCKED state
useAppAccess()shellThis 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()shellSubstrate health, for an operator surface
useStoragePosture()storageWhat the signed-in owner's storage key can hold - allowance, credits, whether room can be bought
useEnableStorage()storageCreates the owner's storage key in their session and records it
usePrivyStorageSigner()storageThe signer a custodial session saves with
useStorageTopUp()storageThe card top-up, including the poll that waits for bought room to land
useAutoSaveAs()storageRuns 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.