npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

uitodemo

v0.1.5

Published

Timeline-driven UI demo player for product walkthroughs.

Readme

uitodemo

uitodemo is a React library for showing product demos with real UI.

Instead of exporting a video, you render your actual interface and describe the flow with a timeline. The library replays typing, clicks, scrolling, pauses, and cursor movement so the demo feels interactive while staying easy to maintain.

It works well for:

  • landing pages
  • product walkthroughs
  • onboarding flows
  • documentation examples
  • in-app feature previews

Installation

npm i uitodemo

You can also use:

pnpm add uitodemo
yarn add uitodemo

Quick start

Use the authoring helpers first. They make the API feel much less like an internal engine.

import { DemoPlayer, demo, demoTarget } from "uitodemo";

const steps = demo()
  .focus("search", { cursor: "text" })
  .type("search", "Cold brew", { delay: 90, cursor: "text" })
  .wait(500)
  .click("product-1", { cursor: "pointer", hover: true })
  .build();

export function Example() {
  return (
    <DemoPlayer
      steps={steps}
      isActive
      cursor={{
        enabled: true,
        hideNativeCursor: true,
        size: "md",
        mobileSize: "xl",
      }}
    >
      <div>
        <input {...demoTarget("search")} readOnly defaultValue="" />
        <button {...demoTarget("product-1")}>Open product</button>
      </div>
    </DemoPlayer>
  );
}

Quick path

  1. Build steps with demo().
  2. Mark targets with demoTarget("id") or demo-id="id".
  3. Pass steps or timeline into DemoPlayer.
  4. Turn on isActive.

Cursor sizing

The simulated cursor can now be sized independently for desktop and mobile:

<DemoPlayer
  cursor={{
    enabled: true,
    size: "md",
    mobileSize: "xxl",
  }}
>
  {/* ... */}
</DemoPlayer>
  • size: desktop / fine pointer cursor size
  • mobileSize: mobile / coarse pointer cursor size
  • available sizes: sm, md, lg, xl, xxl
  • the cursor stays enabled on mobile and only disables for prefers-reduced-motion

Recommended convention

Use one target convention only:

  • Public helper: demoTarget("search")
  • DOM output: demo-id="search"

That keeps the authoring API simple and the DOM contract explicit.

Two authoring styles

1) Recommended: builder style

import { DemoPlayer, demo, demoTarget } from "uitodemo";

const steps = demo()
  .focus("email", { cursor: "text" })
  .type("email", "[email protected]", { delay: 80, cursor: "text" })
  .wait(400)
  .click("continue", { cursor: "pointer", hover: true })
  .build();

export function SignupDemo() {
  return (
    <DemoPlayer steps={steps} isActive>
      <form>
        <input {...demoTarget("email")} readOnly defaultValue="" />
        <button type="button" {...demoTarget("continue")}>
          Continue
        </button>
      </form>
    </DemoPlayer>
  );
}

2) Advanced: raw timeline objects

import { DemoPlayer, type DemoTimeline } from "uitodemo";

const timeline: DemoTimeline = [
  { type: "focus", target: "email", cursor: "text" },
  { type: "type", target: "email", value: "[email protected]", delay: 80, cursor: "text" },
  { type: "wait", delay: 400 },
  { type: "click", target: "continue", cursor: "pointer", hover: true },
];

<DemoPlayer timeline={timeline} isActive>{/* ... */}</DemoPlayer>;

Compound components

For a more composable API, you can now split the player into visible pieces:

import {
  DemoControls,
  DemoOverlay,
  DemoPlayer,
  DemoStage,
  demo,
  demoTarget,
} from "uitodemo";

const steps = demo()
  .focus("search", { cursor: "text" })
  .type("search", "Cold brew", { delay: 90, cursor: "text" })
  .click("product-1", { cursor: "pointer", hover: true })
  .build();

export function HeroDemo() {
  return (
    <DemoPlayer steps={steps} isActive>
      <DemoStage>
        <div>
          <input {...demoTarget("search")} readOnly defaultValue="" />
          <button {...demoTarget("product-1")}>Open product</button>
        </div>
      </DemoStage>
      <DemoOverlay />
      <DemoControls />
    </DemoPlayer>
  );
}

Available pieces:

  • DemoStage → the actual interactive demo surface
  • DemoOverlay → cursor layer and centered play/restart overlay
  • DemoControls → bottom playback controls
  • DemoProgress → progress bar primitive you can use separately

What the package includes

  • DemoPlayer for rendering and replaying the demo
  • DemoControls for optional playback controls
  • timeline helpers and playback hooks
  • simulated cursor support
  • testing helpers from uitodemo/testing

Public API

Import the package from the root:

import {
  DemoControls,
  DemoOverlay,
  DemoPlayer,
  DemoProgress,
  DemoStage,
  DEFAULT_DEMO_TIMINGS,
  demo,
  demoTarget,
  type DemoBuilder,
  type DemoCursorConfig,
  type DemoPlayerProps,
  type DemoStep,
  type DemoTimeline,
} from "uitodemo";

API shape

| API | Use it for | |-----|------------| | demo() | Build timelines fluently with autocomplete | | demoTarget("id") | Mark DOM targets and generate demo-id | | steps | Friendly prop name for most usage | | timeline | Advanced/raw timeline authoring | | DemoStage | Stage primitive for compound composition | | DemoOverlay | Cursor and centered overlay primitive | | DemoControls | Drop-in playback controls | | DemoProgress | Standalone progress primitive | | DEFAULT_DEMO_TIMINGS | Shared timing defaults and overrides |

Choose the right API level

| If you want... | Use | |---|---| | The easiest authoring path | demo() + demoTarget() + steps | | Full low-level control | raw timeline objects | | Timing customization | timings | | Built-in controls | DemoControls or renderControls |

For tests and timeline metadata helpers:

import {
  createTimelineRunner,
  getStepDuration,
  getTimelineDuration,
  resolveDemoTimeline,
} from "uitodemo/testing";

Good use cases

  • show a search flow in a homepage hero
  • preview dashboard interactions before signup
  • explain a feature in docs without recording a video
  • build believable onboarding simulations with real components

Development

Inside this repository, the package lives in packages/uitodemo and the demo site lives in apps/www.

Publish

From the monorepo root:

pnpm publish:uitodemo

Or directly from the package:

cd packages/uitodemo
npm publish --access public