Skip to main content
Urbicon UI
Back to Recipes

Onboarding Flow

First-run onboarding on the Guide system: a pulsing beacon offers an opt-in spotlight tour, a non-modal help panel links back into the UI, a manual hint flags the next feature, and the tour reports its own funnel through onStep, onComplete and onSkip.

Built with GuideProvider Guide GuideBeacon GuidePanel GuideArticle GuideMarker GuideMention GuideHint

Live preview

WorkspacePage.svelte

Click the pulsing beacon to take the three-step tour; the log beside the workspace follows with onStep / onComplete / onSkip, and finishing reveals the New: API keys hint. The ⓘ opens the help panel.
Acme Workspace
New here?

New: API keys

You can now generate scoped API keys for automation.

Tour analytics

onStep / onComplete / onSkip fire from the tour itself: the funnel and drop-off signal onboarding is run for.

No events yet. Start the tour.

<script lang="ts">
  import {
    Guide,
    GuideArticle,
    GuideBeacon,
    GuideController,
    GuideHint,
    GuideMarker,
    GuideMention,
    GuidePanel,
    GuideProvider
  } from '@urbicon-ui/blocks';
  import type { GuideTour } from '@urbicon-ui/blocks';

  const guide = new GuideController();
  let hintOpen = $state(false);

  const onboardingTour: GuideTour = {
    id: 'onboarding',
    // once: false — here, on the beacon and on the hint — keeps the docs demo
    // repeatable. Drop all three in your app: the default (true) shows each
    // once per user, remembered through the controller's storage adapter.
    once: false,
    steps: [
      {
        target: 'projects',
        title: 'Create your first project',
        body: 'Everything starts with a project: your space for tasks, files, and docs.'
      },
      {
        target: 'team',
        title: 'Invite your team',
        body: 'Bring teammates in so they can collaborate from day one.'
      },
      {
        // interactive keeps the spotlit tile clickable during this step
        target: 'api',
        title: 'Generate an API key',
        body: 'Automate anything once you are set up.',
        interactive: true
      }
    ],
    // Stand-ins for your analytics calls — the demo pipes the same events into
    // the log beside the workspace.
    onStep: (e) =>
      analytics.track('onboard_step', { step: e.index + 1, total: e.total, via: e.via }),
    onComplete: () => {
      analytics.track('onboard_complete');
      hintOpen = true; // reveal the "new feature" hint once onboarding is done
    },
    onSkip: (e) => analytics.track('onboard_skip', { droppedAt: e.index + 1 })
  };
</script>

<GuideProvider controller={guide}>
  <!-- Your app shell — the provider wraps it once, near the root. -->
  <header>
    <span>Acme Workspace</span>
    <!-- the ⓘ: opens the help panel at the matching article -->
    <GuideMarker for="projects" />
    <!-- the opt-in tour entry; hides itself once the tour is seen -->
    <GuideBeacon tour={onboardingTour} once={false} />
  </header>

  <!-- data-guide marks each target once; tour steps, the marker, the mentions
       and the hint all resolve to it -->
  <button data-guide="projects">New project</button>
  <button data-guide="team">Invite team</button>
  <button data-guide="api">API keys</button>

  <!-- non-modal help: the workspace stays usable behind the open panel -->
  <GuidePanel title="Workspace help">
    <GuideArticle id="projects" title="Projects & workspace">
      <p>
        A <GuideMention for="projects">project</GuideMention> groups your tasks, files, and
        docs. Start there, then add people from
        <GuideMention for="team">team settings</GuideMention>.
      </p>
      <p>
        Prefer automation? Generate an
        <GuideMention for="api">API key</GuideMention> and drive everything from the API.
      </p>
    </GuideArticle>
  </GuidePanel>

  <!-- waits for onComplete to raise hintOpen -->
  <GuideHint
    for="api"
    trigger="manual"
    open={hintOpen}
    once={false}
    title="New: API keys"
    onDismiss={() => (hintOpen = false)}
  >
    You can now generate scoped API keys for automation.
  </GuideHint>

  <!-- the tour renderer: mount once; renders nothing until a tour starts -->
  <Guide />
</GuideProvider>

Opt-in, drop-off, top layer

The tour is offered, not imposed

An auto-starting tour interrupts everyone to help a few. GuideBeacon waits instead: it pulses beside the header until clicked, and with once (the default) the controller remembers a finished or skipped tour by its id, so nobody is toured twice. That memory lives in a storage adapter: localStorage out of the box, swappable through the controller's storage option when seen-state should follow the account instead of the browser.

Skip is a signal

onSkip reports the step index where the user dropped off, and it fires however the tour ends early: the Skip button, Escape, or a foreign modal taking over. Programmatic teardown via stopTour() stays silent on purpose, so a route change does not count as a lost user.

The overlays out-stack the app

The tour's spotlight and the hint render in the native popover top layer: they clear whatever stacking contexts your app builds, which is also why they may reach over this docs page. A foreign modal above them pauses the tour and hides the hint rather than fighting for z-index.

The tour above is the smallest one. Steps can also gate on the user's real action (advance: 'action') or live on another route (route, with a navigate hook wired to goto); the Guide page documents both, together with the panel's search and article groups.