Embed Widget

All plans

Add interactive step-by-step wizards to any page with a single line of code. No build step, no dependencies, no framework lock-in.

Interactive firstrun guide available

Step-by-step with copy-to-clipboard and progress tracking.

HTML

Paste this anywhere in your HTML. The widget renders a button right where the tag is placed.

<script
  src="https://firstrun.dev/embed.js"
  data-wizard="your-wizard-slug"
></script>

Replace your-wizard-slug with your slug. Find it in the URL when viewing a wizard: firstrun.dev/w/{slug}, or click Embed on the wizard detail page to get the snippet pre-filled.

With all options

<script
  src="https://firstrun.dev/embed.js"
  data-wizard="my-setup-guide"
  data-theme="dark"
  data-button-text="Get started"
></script>

React, Next.js & Vue

Frameworks strip raw <script> tags from JSX. Instead, load embed.js once and use a <div> marker wherever you want a wizard.

Next.js

import Script from "next/script";

export default function DocsPage() {
  return (
    <div>
      <h1>Getting Started</h1>

      {/* Wizard renders here */}
      <div data-firstrun="your-wizard-slug" />

      {/* Load embed.js once per page */}
      <Script
        src="https://firstrun.dev/embed.js"
        strategy="lazyOnload"
      />
    </div>
  );
}

React (Vite, CRA)

import { useEffect } from "react";

function DocsPage() {
  useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://firstrun.dev/embed.js";
    document.body.appendChild(script);
    return () => script.remove();
  }, []);

  return (
    <div>
      <h1>Getting Started</h1>
      <div data-firstrun="your-wizard-slug" />
    </div>
  );
}

Vue

<template>
  <div>
    <h1>Getting Started</h1>
    <div data-firstrun="your-wizard-slug" />
  </div>
</template>

<script setup>
import { onMounted } from "vue";

onMounted(() => {
  const s = document.createElement("script");
  s.src = "https://firstrun.dev/embed.js";
  document.body.appendChild(s);
});
</script>

You can place multiple data-firstrun divs on the same page — the script picks them all up automatically.

README & Markdown

GitHub and most markdown renderers don't allow scripts or iframes. Use a linked badge instead:

[![Firstrun wizard](https://img.shields.io/badge/firstrun-interactive_guide-10b981?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyIj48cG9seWdvbiBwb2ludHM9IjUgMyAxOSAxMiA1IDIxIDUgMyIvPjwvc3ZnPg==)](https://firstrun.dev/w/your-slug)

Replace your-slug with your wizard's slug. Here's what the badge looks like: Firstrun badge

Clicking the badge takes users directly to the full interactive wizard on Firstrun.

Attributes

AttributeRequiredDescription
data-wizardYes *Wizard slug. Use on the <script> tag for HTML embeds.
data-firstrunYes *Wizard slug. Use on a <div> marker for framework embeds (React, Next.js, Vue).
data-themeNo"light", "dark", or "auto" (default). Controls the trigger button appearance. The wizard inside the modal follows the user's system preference.
data-button-textNoCustom button label. Default: Follow the wizard

* Use data-wizard on script tags, data-firstrun on div markers. Pick one approach per embed.

How It Works

Full wizard experience

The embed opens the same wizard page your users see on Firstrun — environment detection, step feedback, progress bar, copy-to-clipboard, hints, and verification blocks all work.

CSS Isolation

The trigger button renders inside a Shadow DOM container. Your styles never affect it, its styles never affect your page. The wizard itself loads in an iframe.

No cookies or localStorage

Nothing is persisted to the user's browser by the embed script itself. No cookies, no tracking across page loads.

Zero API calls

The embed script makes no cross-origin API requests. It renders a button and opens an iframe — no CORS configuration needed.

Content Security Policy

Most sites work out of the box. If your site has a strict CSP, add these to your headers:

script-src https://firstrun.dev;
frame-src https://firstrun.dev;

script-src allows loading embed.js. frame-src allows the wizard iframe.