Persist authenticated sessions with Profiles
Maintain authenticated sessions across Steel browser instances using profiles.
A Steel profile is a named, long-lived browser identity. It holds everything a real Chrome user profile accumulates over time: cookies, localStorage, IndexedDB, history, installed extensions, autofill, site permissions. Every session you attach to the profile starts where the last one left off, and writes the user data directory back on release.
Two options on sessions.create wire it up. On the first run, mint a fresh profile:
session = await client.sessions.create({persistProfile: true,profileId: undefined,});const profileId = session.profileId;
persistProfile: true tells Steel to snapshot the browser data directory when the session ends. profileId: undefined means "create a new one." After the session is created, session.profileId holds the identifier. Store it. Every later run passes it back:
session = await client.sessions.create({persistProfile: true,profileId,});
The new browser opens as that identity. client.profiles.list(), client.profiles.retrieve(id), and client.profiles.delete(id) round out the surface.
How the demo works
index.ts uses demowebshop.tricentis.com, a public shopping cart demo that stores cart state in cookies. The flow lives in main() and three helpers in utils.ts:
- 1
selectOrCreateProfilecallsclient.profiles.list(), theninquirerprompts you to pick an existing profile or create a new one. Returnsundefinedto signal "mint a fresh one." - 2On a fresh run, Session #1 launches with
persistProfile: trueand noprofileId.addItemsToCartvisits three category pages (books, digital downloads, notebooks) and clicks the first add-to-cart button on each. Session #1 releases; Steel writes the profile snapshot. - 3Session #2 launches with the same
profileId.checkItemsInCartopens/cartand reads the rows. Same identity, different browser, same cart.
Select the saved profile on a later run and the menu skips step 2 entirely. One session spins up, finds the cart intact, and exits. That is the test: state survives across distinct sessions because the profile carries it.
Run it
cd examples/profilescp .env.example .env # set STEEL_API_KEYnpm installnpm start
Get a key at app.steel.dev/settings/api-keys. Session viewer URLs print as the script runs. Open them in other tabs to watch each browser.
Your first-run output varies. Structure looks like this:
Steel Profiles Demo============================================================? Select a profile to use: Create a new profileSteel Session #1 created!View session at https://app.steel.dev/sessions/ab12cd34...Profile ID: prof_9f3c...Successfully logged inAdded item from Book categoryAdded item from Digital Download categoryAdded item from Notebook category3 items in cartItems added:1. Computing and Internet2. Music 23. FictionSession #1 releasedSteel Session #2 created!View session at https://app.steel.dev/sessions/ef56gh78...Found 3 items in cartFound your shopping cart!Session released
Full round-trip takes ~60 seconds. A second run against the saved profile takes ~30 seconds because step 2 is skipped.
Sessions go through client.sessions.release() in the finally block. Skipping it keeps browsers running until the 5-minute default timeout and delays the profile snapshot.
What persists
Profiles capture the full Chromium user data directory, not just cookies:
- Cookies and localStorage for every origin you visited.
- Login sessions you kept alive (bank, SaaS dashboard, email).
- IndexedDB entries for apps that cache state client-side.
- Installed extensions and their configuration.
- Autofill, history, bookmarks, site permissions.
Treat the profile like an account. Anyone who can call sessions.create({ profileId }) on your workspace can drive a browser logged in as you. Rotate or delete with client.profiles.delete(id) when the identity is done.
Make it yours
- Swap the target site. Replace the URLs in
login,addItemsToCart, andcheckItemsInCart. The profile plumbing does not change. - Seed a profile interactively. Create a session with
persistProfile: true, open the live viewer, sign in by hand, close the session. The profile keeps the login. Every scripted run after that reuses it. - One profile per identity. If you automate three accounts on the same site, create three profiles. Sharing a profile across accounts means one session's writes overwrite another's state on release.
- Read without writing back. Pass
persistProfile: falsewith an existingprofileIdto load the profile without snapshotting changes on release. Useful for risky runs that might corrupt state.
Related
Three recipes handle "start the browser already signed in." Pick by lifetime:
- credentials: Steel stores a username and password per origin and fills the login form each session. No browser state persists. Good when the form is standard and you want a stable, long-lived setup.
- auth-context: one-shot JSON snapshot of cookies and localStorage you capture from one session and replay into the next. Good when you log in once (SSO, MFA, magic link) and want to move the resulting state forward.
- Profiles (this recipe): long-lived named identity that accumulates everything (history, extensions, preferences, logins) across runs. Good when the browser itself is the unit of persistence.
Related recipes
Reuse authenticated sessions across browsers
Maintain authenticated sessions across Steel browser instances by capturing and reusing cookies and local storage.
Automate logins with the Credentials API
Use the Steel Credentials API with Playwright to automate flows with stored credentials.
Upload and run browser extensions
Use the Steel Extensions API with Playwright to upload and run browser extensions.