Ship public state to production
In the earlier tutorials you pushed public state to the development environment and read it from an app. Real apps serve people from production. This tutorial explains how SkyState environments work, then takes you through one promotion end to end: preview the change, then apply it to move the welcomeText key from staging to production.
Estimated time: 15 minutes.
What you'll build
You carry the welcomeText key from a tutorial track through to production:
- Wire the provider so a build variable picks the environment, instead of a value fixed in the source.
- Register callback URLs for deployed environments, so sign-in and user state keep working outside local development.
- Push a staging welcome message with the CLI.
- Preview the promotion with
sky state public promote --dry-runbefore anything is written. - Apply the promotion at the confirmation prompt, and check the result in production.
- See where
--yesand--commentfit for automation and audit trails.
Prerequisites
- One tutorial track: React, Vanilla, or API. You need its project created and
welcomeTextbeing read from public state. - The SkyState CLI, signed in with
sky login.
Step 1: Understand the environment model
SkyState has three environments: development, staging, and production. Each keeps its own copy of a project's public state, and they stay separate. A push to one never touches the other two.
Every command takes the environment through --env, and the short aliases dev, stg, and prod resolve to the full names:
bash
sky state public push --env staging ... # same as --env stgThe promote command copies state from one environment to another:
bash
sky state public promote \
--project tutorial-app \
--from staging \
--to productionA promotion goes in three moves: a dry-run shows what would change without writing, you read the changes, and a [y/N] prompt asks you to confirm. When you apply, the CLI fetches both environments fresh and recomputes the changes, so it works from the latest production state, not the one the dry-run showed.
Step 2: Pick the environment from a build variable
Whichever track you came from, you wired the environment as development for the first run. That is fine to start, but a production build should reach the production environment, not development. The SDK never reads the environment on its own. It uses whatever you pass and falls back to development when you pass nothing, so drive that value from a build variable instead of fixing it in the source. The variable name is yours to choose; what counts is that the value reaching SkyState is development, staging, or production.
The React snippet below shows the shape. On the Vanilla or API track, read the same build variable and pass its value where you set the environment, instead of editing src/main.tsx.
Update the provider in src/main.tsx:
tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import { SkyStateProvider } from '@skystate/react'
import App from './App.tsx'
const skyStateEnv = import.meta.env.VITE_SKYSTATE_ENV ?? 'development'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<SkyStateProvider
account="YOUR_ACCOUNT_ID"
project="tutorial-app"
environment={skyStateEnv}
callbackUrl={window.location.origin}
>
<App />
</SkyStateProvider>
</StrictMode>,
)VITE_SKYSTATE_ENV is a name you choose, not anything SkyState knows about. What counts is that the value reaching the prop is development, staging, or production. Set it per build:
bash
# local: a .env file in the project
VITE_SKYSTATE_ENV=development
# staging build
VITE_SKYSTATE_ENV=staging npm run build -- --mode staging
# production build
VITE_SKYSTATE_ENV=production npm run buildINFO
Vite's own import.meta.env.MODE is production for any vite build, staging included. Lean on MODE alone and your staging and production builds both come out as production. A variable of your own keeps them apart.
Step 3: Register deployed callback URLs
Public state does not need sign-in, but the React and vanilla tutorial apps add user state after Part 2. User state needs sign-in, and sign-in needs a callback URL for each deployed environment.
Part 2 registered http://localhost:5173 for development only. Register the deployed staging and production URLs before you test sign-in there:
bash
sky project auth callback-urls add \
--project tutorial-app \
--url https://your-staging-app.example.com \
--env staging
sky project auth callback-urls add \
--project tutorial-app \
--url https://your-production-app.example.com \
--env productionRegister the exact origin your app serves from, with no trailing slash. callbackUrl={window.location.origin} sends that bare origin, so the registered URL must match it exactly, just like the development URL from Part 2.
Step 4: Push a staging welcome message
Set welcomeText in staging. This is the state you promote to production next.
bash
sky state public push \
--json '{"welcomeText":"New dashboard is live. Try it now."}' \
--project tutorial-app \
--env staging \
--comment "Prepare welcome text for production launch"Confirm it landed:
bash
sky state public show --project tutorial-app --env stagingStep 5: Preview the promotion
A dry-run shows the change without writing it:
bash
sky state public promote \
--project tutorial-app \
--from staging \
--to production \
--keys welcomeText \
--dry-run--keys welcomeText limits the promotion to that one top-level key. The output lists every change it would make: here, a single addition, since production has no state yet. The dry-run exits without writing anything.
TIP
Use diff when you only want to compare two environments, without preparing a promotion:
bash
sky state public diff \
--project tutorial-app \
--from staging \
--to production \
--keys welcomeTextIt prints the public-state differences and never writes to either environment.
Step 6: Apply the promotion
Run the same command without --dry-run. The CLI recomputes the changes, shows them as a diff, and asks you to confirm:
bash
sky state public promote \
--project tutorial-app \
--from staging \
--to production \
--keys welcomeText \
--comment "Promote launch welcome text to production"Apply all changes? [y/N]Confirm with y. The CLI applies the changes and prints the environment, the number of changes, and the new version number.
Verify
Check that production now carries the welcome message:
bash
sky state public show --project tutorial-app --env productionThe output should show the welcomeText value from staging. To see the SDK read it, run a production build, or set VITE_SKYSTATE_ENV=production in .env.local and restart the dev server. The app shows the production welcome text.
If your app includes the user-state parts of the tutorial, sign in on the production URL too. The callback URL from Step 3 is what lets SkyState return to the production app after sign-in.
Two more paths worth seeing.
--yes skips the prompt, for CI that has no terminal to answer it:
bash
sky state public promote \
--project tutorial-app \
--from staging \
--to production \
--keys welcomeText \
--yes \
--comment "CI: promote welcome text to production"If two promotions hit the same target at once, the second fails with a conflict error. Run sky state public show --project tutorial-app --env production to see the current state, then promote again.
What you learned
- The provider takes its environment from a build variable, so development, staging, and production builds each reach their own state.
- User-state sign-in needs a callback URL per deployed environment.
- A promotion always follows the same path: preview with
--dry-run, apply at the prompt, then check. --keysnarrows a promotion to specific top-level keys, so you ship one key at a time and leave other production values alone.--yesstands in for the confirmation a CI pipeline cannot give.