// Generated by scripts/generate-showcase.mjs. Do not edit directly. page ScrollspyDetail { layout = "showcase" state playground_color = ctx.url.searchParams.get("pg_color") ?? "primary" state playground_size = ctx.url.searchParams.get("pg_size") ?? "default" state playground_items = JSON.parse(ctx.url.searchParams.get("pg_items") ?? "[{\"id\":\"scrollspy-0-1\",\"key\":\"scrollspy-0-1\",\"value\":\"primary\",\"label\":\"Primary workflow\",\"title\":\"Primary workflow\",\"description\":\"A clean default configuration for everyday product interfaces.\",\"text\":\"A configurable sample message.\",\"href\":\"#scrollspy-demo\",\"actionHref\":\"#scrollspy-demo\",\"actionLabel\":\"View details\",\"icon\":\"icon-[lucide--sparkles]\",\"iconClass\":\"icon-[lucide--sparkles]\",\"variant\":\"primary\",\"status\":\"Active\",\"time\":\"09:30\",\"current\":true,\"selected\":true,\"checked\":true,\"disabled\":false,\"outgoing\":false,\"open\":true,\"number\":1,\"count\":16,\"percentage\":72,\"color\":\"#7c3aed\",\"target\":\"_self\",\"ariaLabel\":\"Open primary workflow 1\",\"items\":[{\"label\":\"Nested option A\",\"value\":\"nested-a\"},{\"label\":\"Nested option B\",\"value\":\"nested-b\"}],\"links\":[{\"label\":\"Documentation\",\"href\":\"#documentation\"},{\"label\":\"API reference\",\"href\":\"#api-reference\"}],\"values\":[\"Included\",\"Standard\"]},{\"id\":\"scrollspy-0-2\",\"key\":\"scrollspy-0-2\",\"value\":\"secondary\",\"label\":\"Additional option\",\"title\":\"Supporting example\",\"description\":\"A clean default configuration for everyday product interfaces.\",\"text\":\"Another configurable message.\",\"href\":\"#scrollspy-demo\",\"actionHref\":\"#scrollspy-demo\",\"actionLabel\":\"View details\",\"icon\":\"icon-[lucide--sparkles]\",\"iconClass\":\"icon-[lucide--sparkles]\",\"variant\":\"primary\",\"status\":\"Active\",\"time\":\"09:30\",\"current\":false,\"selected\":false,\"checked\":false,\"disabled\":false,\"outgoing\":true,\"open\":true,\"number\":2,\"count\":32,\"percentage\":72,\"color\":\"#7c3aed\",\"target\":\"_self\",\"ariaLabel\":\"Open primary workflow 2\",\"items\":[{\"label\":\"Nested option A\",\"value\":\"nested-a\"},{\"label\":\"Nested option B\",\"value\":\"nested-b\"}],\"links\":[{\"label\":\"Documentation\",\"href\":\"#documentation\"},{\"label\":\"API reference\",\"href\":\"#api-reference\"}],\"values\":[\"Included\",\"Standard\"]}]") state playground_active = ctx.url.searchParams.get("pg_active") ?? "" state playground_label = ctx.url.searchParams.get("pg_label") ?? "Scrollspy" state playground_heading = ctx.url.searchParams.get("pg_heading") ?? "" state playground_class = ctx.url.searchParams.get("pg_class") ?? "showcase-instance showcase-instance--1" seo { title = "Scrollspy" description = "Theme-aware, responsive scrollspy component." } view {
Navigation component

Scrollspy

Theme-aware, responsive scrollspy component.

7 props1 slots1 outputsTheme readyResponsive
Interactive playground

Configure Scrollspy

Change any prop and inspect the server-rendered component immediately.

Live preview
Component code
<Scrollspy
  items='[
    {
      "id": "scrollspy-0-1",
      "key": "scrollspy-0-1",
      "value": "primary",
      "label": "Primary workflow",
      "title": "Primary workflow",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "A configurable sample message.",
      "href": "#scrollspy-demo",
      "actionHref": "#scrollspy-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": true,
      "selected": true,
      "checked": true,
      "disabled": false,
      "outgoing": false,
      "open": true,
      "number": 1,
      "count": 16,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 1",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    },
    {
      "id": "scrollspy-0-2",
      "key": "scrollspy-0-2",
      "value": "secondary",
      "label": "Additional option",
      "title": "Supporting example",
      "description": "A clean default configuration for everyday product interfaces.",
      "text": "Another configurable message.",
      "href": "#scrollspy-demo",
      "actionHref": "#scrollspy-demo",
      "actionLabel": "View details",
      "icon": "icon-[lucide--sparkles]",
      "iconClass": "icon-[lucide--sparkles]",
      "variant": "primary",
      "status": "Active",
      "time": "09:30",
      "current": false,
      "selected": false,
      "checked": false,
      "disabled": false,
      "outgoing": true,
      "open": true,
      "number": 2,
      "count": 32,
      "percentage": 72,
      "color": "#7c3aed",
      "target": "_self",
      "ariaLabel": "Open primary workflow 2",
      "items": [
        {
          "label": "Nested option A",
          "value": "nested-a"
        },
        {
          "label": "Nested option B",
          "value": "nested-b"
        }
      ],
      "links": [
        {
          "label": "Documentation",
          "href": "#documentation"
        },
        {
          "label": "API reference",
          "href": "#api-reference"
        }
      ],
      "values": [
        "Included",
        "Standard"
      ]
    }
  ]'
  label="Scrollspy"
/>
Event outputInteract with the preview to inspect the typed payload.
Component props7 controls
Live examples

Designed for real product surfaces

Compare configurations and resize the browser to check responsive behavior.

Recommended

Follows the reader

Scroll the page and the marker moves to whichever section is in view. The sections below are real page content, because the runtime observes against the viewport -- a table of contents inside a small scrolling box would have nothing to react to.

01
Live preview

Overview

Scroll this page and watch the marker in the list move. The runtime observes these sections against the viewport, so the current link follows whatever you are reading.

Installation

Each link points at one of these ids. Nothing is connected up by hand -- the component only renders the links, and the runtime moves aria-current.

Usage

The active link is marked with aria-current="location", which is what a screen reader announces, and styled through data-active.

API

Clicking a link jumps to its section and marks it immediately, so the control responds even before the scroll settles.

Component usage.wrn
<Scrollspy
  items='[
    {
      "label": "Overview",
      "href": "#overview"
    },
    {
      "label": "Installation",
      "href": "#installation"
    },
    {
      "label": "Usage",
      "href": "#usage"
    },
    {
      "label": "API",
      "href": "#api"
    }
  ]'
  active="#overview"
  label="Scrollspy"
  heading="On this page"
/>
Advanced

Compact rail

On a phone the rail becomes a horizontal strip that scrolls, so a long contents list costs one line rather than a screenful.

02
Live preview
Component usage.wrn
<Scrollspy
  size="sm"
  items='[
    {
      "label": "Overview",
      "href": "#overview"
    },
    {
      "label": "Installation",
      "href": "#installation"
    },
    {
      "label": "Usage",
      "href": "#usage"
    }
  ]'
  active="#installation"
  label="Compact example"
  heading="Sections"
/>
Component outputs

Receive every typed component output

Use declarative output handlers in .wrn files or register a direct output handler from JavaScript. The canonical API exposes payload and does not require event.detail.

@changeFires when the component's committed value or selection changes.
Declarative handlers.wrn
<Scrollspy
  @change='console.log(payload)'
/>
Direct output handlers.js
import { registerOutputHandler } from "@wrnexus/csr/outputs"

const component = document.querySelector("[data-ui-component=\"Scrollspy\"], [data-component=\"Scrollspy\"]")

if (component) registerOutputHandler(component, "change", (payload) => {
  console.log("change", payload)
})
Component API

Props and configuration

All content and behavior shown above is supplied through these props and slots.

PropTypeDefaultRequired
colorstring"primary"No
sizestring"default"No
itemsunknown[][]No
activestring""No
labelstring"On this page"No
headingstring""No
classstring""No
} }