DEV Community

Cover image for How we tripled User Activation with an Interactive Tutorial (Svelte 5 Case Study)
Polliog
Polliog

Posted on

How we tripled User Activation with an Interactive Tutorial (Svelte 5 Case Study)

When we launched LogWard as an open-source, privacy-first log management platform, we faced a classic SaaS challenge: The "Empty State" Problem.

We helped users create an account, but then... silence.

Our initial approach was standard developer documentation—comprehensive, detailed, and... largely ignored. Users would sign up, see an empty dashboard, and leave. The data told a painful story: only 23% of new users were actually sending their first log within the first session.

We needed to change something fundamental.

Here is how we built an interactive onboarding flow using Svelte 5 Runes that jumped our activation rate to 67%.

The Problem: Too Many Steps, Too Little Guidance

Setting up a log management system isn't trivial. To get value, a user needs to:

  1. Create an organization
  2. Set up a project
  3. Generate an API key
  4. Integrate our SDK into their application
  5. Send their first log
  6. Understand what they can do with the data

Each step is a friction point. Without guidance, users were getting lost. We saw patterns in support requests:

"Where do I find my API key?"

"I sent logs but don't see them in the dashboard."

"What's the difference between a project and an organization?"

The traditional solution—"write better docs"—wasn't working. People don't read docs until they're stuck. By then, they might have already churned.

The Solution: An Interactive First-Run Tutorial

We decided to build an overlay tutorial that guides new users through their first successful log ingestion. The goal wasn't to teach them everything about LogWard—it was to get them to their "Aha! Moment" as quickly as possible.

Design Principles

1. Progress Over Perfection

We broke the onboarding into 6 distinct steps. Each step is atomic—users can't fail because we validate inputs in real-time.

2. Show, Don't Tell

Instead of describing what an API key does, we generate one for them and show exactly how to use it with copy-pasteable code:

# We provide ready-to-use code snippets with THEIR key pre-filled
curl -X POST https://api.logward.dev/api/v1/ingest \
  -H "X-API-Key: lwk_..." \
  -H "Content-Type: application/json" \
  -d '{"level":"info","message":"Hello from my app!"}'
Enter fullscreen mode Exit fullscreen mode

3. Celebrate Small Wins

Every completed step gives visual feedback—checkmarks, progress bars, encouraging messages. We're not just showing progress; we're building confidence.

4. Respect User Autonomy

Not everyone wants a tutorial. We provide a prominent "Skip tutorial" button, and we persist progress so users can return later.

The Tech Stack: Svelte 5 Runes Implementation

Since LogWard is built with SvelteKit 5, we used the new Runes system ($state, $derived) to manage the tutorial state. It turned out to be much cleaner than the old Svelte 4 stores.

1. The Global State (Shared Rune)

Instead of a writable store, we use a class with $state. This makes the logic portable and easy to test:

// lib/stores/onboarding.svelte.ts

export type OnboardingStep = 
  | 'welcome' | 'create-organization' | 'create-project' 
  | 'api-key' | 'first-log' | 'completed';

const STEP_ORDER: OnboardingStep[] = [
  'welcome', 'create-organization', 'create-project', 
  'api-key', 'first-log', 'completed'
];

export class OnboardingState {
    // Svelte 5 Reactivity
    currentStep = $state('welcome');
    isSkipped = $state(false);

    // Derived state (replaces derived stores)
    progressPercent = $derived.by(() => {
        const idx = STEP_ORDER.indexOf(this.currentStep);
        return Math.round((idx / (STEP_ORDER.length - 1)) * 100);
    });

    constructor(initialState?: { step: OnboardingStep, skipped: boolean }) {
        if (initialState) {
            this.currentStep = initialState.step;
            this.isSkipped = initialState.skipped;
        }
    }

    next() {
        const idx = STEP_ORDER.indexOf(this.currentStep);
        if (idx < STEP_ORDER.length - 1) {
            this.currentStep = STEP_ORDER[idx + 1];
            this.syncToServer();
        }
    }

    skip() {
        this.isSkipped = true;
        this.syncToServer();
    }

    async syncToServer() {
        // Persist state to backend so it works across devices
        await fetch('/api/v1/user/onboarding', {
            method: 'POST',
            body: JSON.stringify({ 
                step: this.currentStep, 
                skipped: this.isSkipped 
            })
        });
    }
}

// Singleton instance for the app
export const onboarding = new OnboardingState();
Enter fullscreen mode Exit fullscreen mode

2. The UI Component

Consuming this state in components is incredibly simple now. No $ subscriptions needed for the logic, just read the property:

<script lang="ts">
  import { onboarding } from '$lib/stores/onboarding.svelte';
  import { CheckCircle2 } from 'lucide-svelte';
</script>

<div class="relative w-full">
  <div class="h-1 bg-muted rounded-full overflow-hidden">
    <div
      class="h-full bg-primary transition-all duration-500 ease-out"
      style="width: {onboarding.progressPercent}%"
    ></div>
  </div>

  <div class="flex justify-between mt-2">
     <span>Current Progress: {onboarding.progressPercent}%</span>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

The beauty of Svelte 5 Runes is that you get automatic reactivity without the ceremony of stores. When onboarding.currentStep changes, all components that read it automatically update.

The Results 📈

After deploying the interactive tutorial in version v0.2.2, the metrics shifted dramatically:

Activation Rate: 23% → 67%

More than twice as many users now successfully send their first log within the first session.

Time to First Log: ~15 min → ~3 min

The guided flow eliminates decision paralysis.

Support Tickets: -40%

Most "getting started" questions disappeared because the tutorial answers them proactively.

User Feedback

"Finally, a logging tool that doesn't assume I'm a DevOps expert. The tutorial got me set up in minutes."

"I appreciated being able to skip the tutorial but still have it available later. Respectful of my time."

Lessons Learned

Measure What Matters: We initially tracked "tutorial completion rate", but that was vanity. Users who skip the tutorial but still send logs are successful users. We shifted to tracking "Time to First Log" regardless of the path taken.

Mobile Matters: We almost shipped desktop-only. Last-minute testing revealed that 35% of signups happen on mobile. We rebuilt the modal to be responsive, and it paid off.

Validation is Key: We used Playwright to test the tutorial flow end-to-end (including edge cases like refreshing the page mid-tutorial). This caught bugs that would have frustrated new users.

Try it Yourself

LogWard is open-source (AGPLv3). You can check out the source code to see how we implemented the onboarding flow, or try the tutorial yourself on the free cloud tier.


Have you built similar onboarding systems? Did you find that "Hand-holding" users increased conversion, or did it annoy power users? Let me know in the comments! 👇

Top comments (0)