Skip to main content
A zone is UI that lives outside the individual screens and stays put as the user moves between them. Zones are the right place for a back button, a progress bar, a sticky “Continue” footer, or a floating badge. You build a zone the same way you build a screen: it holds a regular component tree.
Zones replace the old chrome model. FlowDefinition.chrome is deprecated and only kept so old flows keep working. Always build persistent UI with zones. If you see “chrome” anywhere, treat it as the legacy name for the same idea.

The three zones

A flow has one optional persistentUI object with up to three zones:
ZoneSchema fieldWhere it sitsNotes
Navigation BarnavigationBarPinned to the topOccupies layout space. Content sits below it.
FooterfooterPinned to the bottomOccupies layout space. Content sits above it.
OverlayoverlayFloating layer over the contentDoes not push content up or down. Rendered on top.
The navigation bar and footer hold a normal ComponentNode tree (usually a stack with buttons, text, or a progress bar). The overlay is the same, but it is drawn as a floating layer rather than taking up vertical space.

Turn on zones

Zones live in the Layers panel, above the screen list.
1

Open the Layers panel

In the editor, open the left Layers tab. The persistent zones section is at the top, before the list of screens.
2

Click Add Persistent Zones

If a flow has no zones yet, you see a single Add Persistent Zones button. Click it.
3

Two zones appear

This creates a Navigation Bar and a Footer, both visible and empty, each with safe area turned on and a transition mode of Persistent. They show as collapsible rows in the Layers panel.
Add Persistent Zones creates only the navigation bar and footer. The current editor build does not provide a control to add the overlay zone from this panel. The overlay is a real, supported zone (the iOS SDK renders it), but it only appears in the editor if the flow’s JSON already defines it (for example from a template or a hand edited schema). Treat the overlay as schema and SDK level for now.
TODO: confirm whether an “add overlay” control is planned for the editor (see components/editor/PersistentZonesSection.tsx, initializePersistentUI in lib/stores/flow-json-store.ts).

Add components to a zone

Once a zone exists, fill it like a screen.
  • Drag from Insert. Select the zone (or a stack inside it), then open Insert and drag a Component or a Block in. The Chrome blocks (Back Button, Close Button, Progress Bar) are built for zones.
  • Use the zone menu. Hover a zone row and click the ... (more) icon, then Add Element and pick a component.
  • Reorder. Drag component rows within the zone to reorder, or drop them into a stack to nest them.
The Chrome blocks are not special component types. They are ordinary nodes with preset settings:
BlockComponentBehavior
Back Buttonbutton (icon ChevronLeft)onPress runs goBack
Close Buttonbutton (icon X)onPress runs closeFlow
Progress Barprogress in auto modeAuto-tracks flow progress
See Blocks for the full catalog and Components for the primitive list.

Selecting a zone vs a component in it

Clicking a zone row selects the zone’s root stack and shows the zone’s properties. Clicking a component row inside the zone selects that component and shows its properties. The breadcrumb at the top of the properties panel tells you which zone you are in (Navigation Bar, Footer, or Overlay). Each zone row also has an eye toggle that shows or hides the whole zone across the entire flow. Each component inside a zone has its own eye toggle. These are global to the flow, not per screen (for per screen control, see Per-screen overrides below).

Zone properties

Select a zone (click its row) to edit the zone. The panel is the same Layout / Style / Logic layout used for screen components, plus two zone level controls at the top (navigation bar and footer only):
ControlWhat it setsSchema
Safe AreaRespect the device safe area (notch at top, home indicator at bottom) so content does not sit under it. On by default.props.safeArea
TransitionHow the zone behaves when the screen content changes. Defaults to Persistent.behavior.transitionMode
Everything else about a zone is set on its root stack:
  • Background: select the zone’s root stack, open the Style tab, and use the Layer section (set a fill and color). The renderer uses the zone background, falling back to the root stack’s background.
  • Padding: use the root stack’s Spacing section. Safe area is added on top of this padding, not instead of it.
  • Layout (axis, alignment, spacing): the root stack’s Stack Settings, exactly like any other stack. See Layout and sizing.
The overlay zone does not show the Safe Area or Transition controls. Its alignment and touch behavior are schema properties (see Overlay alignment and passthrough).

Transition modes

The Transition dropdown on the navigation bar and footer offers four modes, defined in the schema as:
ModeMeaning
persistentZone stays visually fixed. No animation. This is the default and the most common choice.
crossfadeThe zone’s content cross-fades when it changes between screens.
reflowLayout changes (size, position) animate smoothly.
participateThe zone transitions together with the screen content, inside the animation container.
The iOS SDK currently implements two behaviors, not four. It animates the zone on a screen change only for crossfade and reflow (both use the same zone animation), and keeps the zone fixed for persistent and participate. So today reflow behaves like crossfade, and participate behaves like persistent. Whether the whole flow transitions as one unit is governed flow wide by settings.persistDuringTransition (default true, zones stay fixed), which the editor does not expose. Verified against Presentation/FlowPresenter.swift (shouldAnimateZone) and Rendering/ZoneRenderer.swift.
TODO: confirm with the team whether distinct reflow and per zone participate rendering is planned.
Use persistent unless you have a clear reason to animate the bar. Animating a navigation bar or footer on every screen change is usually distracting.

Overlay alignment and passthrough

The overlay zone has two extra schema properties that control how it floats:
PropertyValuesDefaultMeaning
alignmenttopLeading, top, topTrailing, leading, center, trailing, bottomLeading, bottom, bottomTrailingbottomTrailingWhere the overlay content sits within the content area.
passthroughtrue / falsefalseWhen true, taps pass through the overlay to the content below. When false, the overlay blocks taps in its area.
These two properties are honored by the iOS SDK and the editor preview, but the editor does not currently surface controls for them. To set overlay alignment or passthrough, you edit the flow schema directly. Verified: alignment and passthrough appear only in the renderers (Rendering/OverlayRenderer.swift, components/editor/canvas/CanvasRenderer.tsx), not in any properties panel.
TODO: re-check when overlay editing lands in the editor UI.

Per-screen overrides

By default a zone shows on every screen. You can change that per screen. In the editor, select a screen and open its Behavior & Layout section (see The properties panel). It has one zone control:
  • Hide All Zones (screenSettings.hideAllZones): hides the navigation bar, footer, and overlay on this one screen. Off by default. This is the usual way to give a welcome or success screen a clean, full bleed look.
The schema supports finer per screen control that the editor does not yet expose:
CapabilitySchema fieldStatus
Hide a single zone on one screenscreenSettings.navigationBar.visible (and footer, overlay)SDK and schema only, no editor control
Replace a zone’s layout on one screenscreenSettings.navigationBar.replaceLayoutSDK and schema only, no editor control
Override the progress value on one screenscreenSettings.progressOverride (0 to 1, or null for auto)SDK and schema only, no editor control
A zone only allows per screen overrides if its behavior.allowScreenOverride is true (the default). The editor sets this on when it creates the zones and does not expose a toggle for it.

Progress bars in a zone

A Progress Bar block in a zone runs in auto mode, which tracks the user’s position through the flow automatically. Two settings on the screen affect the value it shows:
  • Include in Progress Steps (includeInProgress, on by default): whether this screen counts toward the total. Turn it off for screens that should not advance the bar (for example an interstitial).
  • Progress override (screenSettings.progressOverride): forces a specific value (0 to 1) on a screen. Schema and SDK only for now (see the table above).
Both are documented on The properties panel. Goal: a back button and a progress bar that stay across every step, hidden on the welcome screen, plus a sticky “Continue” footer.
1

Add the zones

In the Layers panel, click Add Persistent Zones. You now have a Navigation Bar and a Footer.
2

Fill the navigation bar

Select the Navigation Bar. Its root stack is already horizontal with space between its children. Open Insert, then Blocks, then Chrome, and add a Back Button and a Progress Bar. Drag them so the back button is on the left and the progress bar is centered.
3

Fill the footer

Select the Footer. Add a Button (from Insert), set its label to “Continue”, and on the Logic tab add an On Tap interaction with a goNext action. Because it is in the footer, it stays pinned to the bottom on every screen.
4

Hide the zones on the welcome screen

Select your first (welcome) screen. In the properties panel open Behavior & Layout and turn on Hide All Zones. The welcome screen now renders full bleed, with no bar or footer.
5

Preview

Open Preview (⌘.). Move past the welcome screen and the bar and footer appear and stay fixed while the screen content transitions. The progress bar fills as you advance.
The resulting persistentUI looks like this in the flow schema:
{
  "persistentUI": {
    "navigationBar": {
      "id": "zone_navbar_1",
      "visible": true,
      "layout": {
        "id": "navbar_stack",
        "type": "stack",
        "props": { "direction": "horizontal", "align": "center", "justify": "space-between" },
        "children": [
          {
            "id": "back_btn",
            "type": "button",
            "props": { "iconName": "ChevronLeft", "backgroundColor": "transparent" },
            "interactions": [
              { "id": "i1", "event": "onPress", "actions": [{ "kind": "goBack" }] }
            ]
          },
          {
            "id": "progress_1",
            "type": "progress",
            "props": { "mode": "auto", "color": "#4F46E5", "height": 4 }
          }
        ]
      },
      "behavior": { "allowScreenOverride": true, "transitionMode": "persistent" },
      "props": { "safeArea": true }
    },
    "footer": {
      "id": "zone_footer_1",
      "visible": true,
      "layout": {
        "id": "footer_stack",
        "type": "stack",
        "props": { "direction": "vertical", "align": "stretch", "justify": "flex-end" },
        "children": [
          {
            "id": "continue_btn",
            "type": "button",
            "props": { "text": "Continue" },
            "interactions": [
              { "id": "i2", "event": "onPress", "actions": [{ "kind": "goNext" }] }
            ]
          }
        ]
      },
      "behavior": { "allowScreenOverride": true, "transitionMode": "persistent" },
      "props": { "safeArea": true }
    },
    "settings": { "persistDuringTransition": true }
  }
}
The welcome screen carries the override:
{
  "id": "screen_welcome",
  "kind": "screen",
  "name": "Welcome",
  "layout": { "id": "root", "type": "stack" },
  "screenSettings": { "hideAllZones": true }
}
An overlay zone (schema only for now) that floats a badge in the top trailing corner and lets taps reach the content below:
{
  "overlay": {
    "id": "zone_overlay_1",
    "visible": true,
    "layout": { "id": "overlay_stack", "type": "stack", "children": [] },
    "behavior": { "allowScreenOverride": true },
    "props": { "alignment": "topTrailing", "passthrough": true }
  }
}

Common mistakes

  • Putting persistent navigation inside a screen. A back button or progress bar added to a screen’s Root Stack does not persist. It is rebuilt and re-animated on every screen change, and it disappears when you navigate. Put anything that should stay across screens in a zone.
  • A non-passthrough overlay blocking the UI. An overlay with passthrough: false blocks taps in its whole area. If the overlay fills the content area, it can swallow taps meant for the screen. Keep the overlay content small, or set passthrough: true.
  • Forgetting safe area. With safe area off, navigation bar content can sit under the notch and footer content under the home indicator. Leave Safe Area on unless you are deliberately drawing into those areas.
  • Animating zones by habit. Setting Crossfade or Reflow makes the bar move on every screen change. For most flows persistent (fixed) reads better.
  • Expecting per zone hide in the editor. The only per screen zone control in the editor is Hide All Zones. Hiding just the footer on one screen is a schema level capability today.