From f6d19c08776db55e49f51ab7dd0714d47d763994 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 11:41:56 +0100 Subject: [PATCH 01/21] docs: add documentation redesign design document Comprehensive plan for restructuring TanStack Devtools documentation with layered architecture approach: Getting Started, Concepts, Guides, API Reference, and Examples sections. Adds 11 new pages including architecture overview, event system deep-dive, Vue framework support, and plugin building guides. --- docs/plans/2026-03-04-documentation-design.md | 300 ++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 docs/plans/2026-03-04-documentation-design.md diff --git a/docs/plans/2026-03-04-documentation-design.md b/docs/plans/2026-03-04-documentation-design.md new file mode 100644 index 00000000..e99b2862 --- /dev/null +++ b/docs/plans/2026-03-04-documentation-design.md @@ -0,0 +1,300 @@ +# TanStack Devtools Documentation Redesign + +**Date:** 2026-03-04 +**Status:** Approved + +## Goals + +1. Serve both end users (app developers using devtools) and plugin authors (building custom devtools) equally +2. Add a conceptual backbone — architecture, event system, plugin lifecycle +3. Fill framework gaps — full Vue documentation, Solid custom plugins guide +4. Keep everything in the existing `docs/` directory for tanstack.com rendering +5. Fix existing issues (TODOs, missing frameworks in quick-start, thin content) + +## Approach + +**Layered Architecture** — progressive disclosure from "get running in 2 minutes" to "build your own devtools from scratch." + +## Documentation Site Map + +### Getting Started (existing section, updated) + +| Page | Status | Changes | +|------|--------|---------| +| Overview | UPDATE | Add package map, architecture diagram, "What's in the box" section | +| Quick Start | UPDATE | Add Vue, Preact examples alongside React/Solid | +| Installation | UPDATE | Add Vue, Preact install instructions | +| Configuration | MINOR | No major changes | +| Plugin Configuration | MINOR | No major changes | +| Vite Plugin | UPDATE | Add feature explanations for source inspector, console piping | +| Production | KEEP | Good as-is | +| Third-party Plugins | MOVE | Move to Guides section | + +### Getting Started — Framework Pages + +| Page | Status | +|------|--------| +| React: Basic Setup | KEEP | +| React: Adapter | UPDATE — replace "TODO" with actual hooks/exports content | +| Preact: Basic Setup | KEEP | +| Preact: Adapter | KEEP | +| Solid: Basic Setup | KEEP | +| Solid: Adapter | KEEP | +| **Vue: Basic Setup** | **NEW** | +| **Vue: Adapter** | **NEW** | + +### Concepts (NEW section) + +| Page | Description | +|------|-------------| +| **Architecture Overview** | Package map, dependency graph, 3-layer model (transport/core/framework), data flow trace | +| **Event System** | EventClient, typed event maps, emit/on/onAll, plugin scoping, connection lifecycle, server bus, debugging | +| **Plugin Lifecycle** | Plugin interface, mount sequence, DOM containers, theme propagation, framework adapter pattern, cleanup | +| **Source Inspector** | How go-to-source works, data-tsd-source attributes, Vite integration, editor configuration | + +### Guides (existing section, expanded) + +| Page | Status | +|------|--------| +| **Building Custom Plugins** | **NEW** — comprehensive framework-agnostic guide | +| **Using devtools-utils** | **NEW** — plugin factory helpers (createReactPlugin, createSolidPlugin, etc.) | +| **Bidirectional Communication** | **NEW** — app-to-devtools and devtools-to-app patterns, time-travel example | +| Third-party Plugins | MOVED from Getting Started | +| React: Custom Plugins | KEEP | +| Preact: Custom Plugins | KEEP | +| **Solid: Custom Plugins** | **NEW** | +| **Vue: Custom Plugins** | **NEW** | + +### API Reference (existing, minor additions) + +| Page | Status | +|------|--------| +| Core API Reference | KEEP (auto-generated) | +| React Reference | KEEP | +| Preact Reference | KEEP | +| Solid Reference | KEEP | +| **Vue Reference** | **NEW** (auto-generated) | + +### Examples (existing, Vue addition) + +| Page | Status | +|------|--------| +| React: Basic, Start, Custom | KEEP | +| Preact: Basic, Custom | KEEP | +| Solid: Basic | KEEP | +| **Vue: Basic** | **NEW** | + +## New Page Content Designs + +### Architecture Overview (`docs/architecture.md`) + +1. **Package Map** — all 11 packages with 1-line descriptions +2. **Dependency Graph** — visual showing: + ``` + Transport: event-bus ← event-bus-client ← devtools-client + Core: devtools (uses devtools-client, event-bus/client, devtools-ui) + Framework: react-devtools, vue-devtools, solid-devtools, preact-devtools (wrap devtools) + Build: devtools-vite (uses devtools-client, event-bus/server) + Utilities: devtools-utils (uses devtools-ui) + ``` +3. **3-Layer Model**: + - Transport layer: event-bus (WebSocket/SSE server + client), event-bus-client (EventClient abstraction) + - Core layer: devtools (Solid.js shell), devtools-ui (components), devtools-client (core events) + - Framework layer: thin wrappers converting framework JSX to DOM render functions +4. **Data Flow Trace** — from `eventClient.emit('my-event', data)` through to devtools panel rendering + +### Event System (`docs/event-system.md`) + +1. EventClient basics — creating a typed client, emit(), on(), onAll() +2. Event Maps — TypeScript generics for type-safe events +3. Plugin scoping — pluginId prefixes, event filtering +4. Connection lifecycle — queue → connect → retry +5. Server event bus — when needed (Vite integration), when not +6. Debugging — debug mode, console output format + +### Plugin Lifecycle (`docs/plugin-lifecycle.md`) + +1. Plugin interface — name, render, destroy, defaultOpen, id +2. Mount sequence — TanStackDevtoolsCore.mount() call chain +3. DOM containers — PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID +4. Theme propagation — 'light'/'dark' passed to render functions +5. Framework adapter pattern — how React/Vue/Solid convert JSX to DOM +6. Cleanup — destroy() callback timing + +### Source Inspector (`docs/source-inspector.md`) + +1. What it does — click any element to jump to source +2. How it works — data-tsd-source attributes injected by Vite plugin +3. Hotkey configuration — inspectHotkey setting +4. Editor integration — launch-editor, custom editor configuration +5. Ignore patterns — files and components to exclude + +### Building Custom Plugins (`docs/building-custom-plugins.md`) + +1. Overview — what a custom plugin is, when to build one +2. Define your event map — TypeScript types +3. Create an EventClient — extending the base class +4. Emit events from your library — integration patterns +5. Build the devtools panel — consuming events, rendering UI +6. Register the plugin — hooking into TanStackDevtools +7. Advanced: Bidirectional communication +8. Advanced: Using devtools-utils factories +9. Advanced: Framework-agnostic plugins +10. Publishing to the marketplace + +### Using devtools-utils (`docs/devtools-utils.md`) + +1. What devtools-utils provides — factory helpers per framework +2. createReactPlugin() — usage, DevtoolsPanelProps, NoOp fallback +3. createSolidPlugin() — usage, DevtoolsSolidClass +4. createVuePlugin() — usage +5. createPreactPlugin() — usage +6. When to use factories vs manual plugin creation + +### Bidirectional Communication (`docs/bidirectional-communication.md`) + +1. Pattern: App emits state, devtools listens +2. Pattern: Devtools sends commands, app listens +3. Pattern: Time-travel (snapshot collection, revert) +4. Complete example with both directions + +### Vue Pages + +**Vue Basic Setup** — mirrors React basic-setup but with Vue template syntax, script setup, and npm install commands +**Vue Adapter** — documents TanStackDevtoolsVuePlugin interface (component instead of render), TanStackDevtoolsVueInit +**Vue Custom Plugins** — same EventClient setup (framework-agnostic), Vue-specific panel component + +### Updated Pages + +**Overview** — add "What's in the box" package list, architecture diagram, links to Concepts +**Quick Start** — add Vue and Preact tabs/sections +**Installation** — add Vue and Preact install commands +**React Adapter** — replace TODO with actual exported types, component props, usage patterns + +## config.json Changes + +The new `config.json` sections array will be: + +```json +[ + { + "label": "Getting Started", + "children": [ + { "label": "Overview", "to": "overview" }, + { "label": "Quick Start", "to": "quick-start" }, + { "label": "Installation", "to": "installation" }, + { "label": "Configuration", "to": "configuration" }, + { "label": "Plugin Configuration", "to": "plugin-configuration" }, + { "label": "Vite Plugin", "to": "vite-plugin" }, + { "label": "Production", "to": "production" } + ], + "frameworks": [ + { "label": "react", "children": [ + { "label": "Basic Setup", "to": "framework/react/basic-setup" }, + { "label": "React Adapter", "to": "framework/react/adapter" } + ]}, + { "label": "preact", "children": [ + { "label": "Basic Setup", "to": "framework/preact/basic-setup" }, + { "label": "Preact Adapter", "to": "framework/preact/adapter" } + ]}, + { "label": "solid", "children": [ + { "label": "Basic Setup", "to": "framework/solid/basic-setup" }, + { "label": "Solid Adapter", "to": "framework/solid/adapter" } + ]}, + { "label": "vue", "children": [ + { "label": "Basic Setup", "to": "framework/vue/basic-setup" }, + { "label": "Vue Adapter", "to": "framework/vue/adapter" } + ]} + ] + }, + { + "label": "Concepts", + "children": [ + { "label": "Architecture Overview", "to": "architecture" }, + { "label": "Event System", "to": "event-system" }, + { "label": "Plugin Lifecycle", "to": "plugin-lifecycle" }, + { "label": "Source Inspector", "to": "source-inspector" } + ] + }, + { + "label": "Guides", + "children": [ + { "label": "Building Custom Plugins", "to": "building-custom-plugins" }, + { "label": "Using devtools-utils", "to": "devtools-utils" }, + { "label": "Bidirectional Communication", "to": "bidirectional-communication" }, + { "label": "Third-party Plugins", "to": "third-party-plugins" } + ], + "frameworks": [ + { "label": "react", "children": [ + { "label": "Custom Plugins", "to": "framework/react/guides/custom-plugins" } + ]}, + { "label": "preact", "children": [ + { "label": "Custom Plugins", "to": "framework/preact/guides/custom-plugins" } + ]}, + { "label": "solid", "children": [ + { "label": "Custom Plugins", "to": "framework/solid/guides/custom-plugins" } + ]}, + { "label": "vue", "children": [ + { "label": "Custom Plugins", "to": "framework/vue/guides/custom-plugins" } + ]} + ] + }, + { + "label": "API Reference", + "children": [ + { "label": "Core API Reference", "to": "reference/index" } + ], + "frameworks": [ + { "label": "react", "children": [ + { "label": "React Reference", "to": "framework/react/reference/index" } + ]}, + { "label": "preact", "children": [ + { "label": "Preact Reference", "to": "framework/preact/reference/index" } + ]}, + { "label": "solid", "children": [ + { "label": "Solid Reference", "to": "framework/solid/reference/index" } + ]}, + { "label": "vue", "children": [ + { "label": "Vue Reference", "to": "framework/vue/reference/index" } + ]} + ] + }, + { + "label": "Examples", + "children": [], + "frameworks": [ + { "label": "react", "children": [ + { "label": "Basic", "to": "framework/react/examples/basic" }, + { "label": "TanStack Start", "to": "framework/react/examples/start" }, + { "label": "Custom Devtools", "to": "framework/react/examples/custom-devtools" } + ]}, + { "label": "preact", "children": [ + { "label": "Basic", "to": "framework/preact/examples/basic" }, + { "label": "Custom Devtools", "to": "framework/preact/examples/custom-devtools" } + ]}, + { "label": "solid", "children": [ + { "label": "Basic", "to": "framework/solid/examples/basic" } + ]}, + { "label": "vue", "children": [ + { "label": "Basic", "to": "framework/vue/examples/basic" } + ]} + ] + } +] +``` + +## Summary of Changes + +**New pages:** 11 +- Concepts: architecture, event-system, plugin-lifecycle, source-inspector (4) +- Guides: building-custom-plugins, devtools-utils, bidirectional-communication (3) +- Framework: vue/basic-setup, vue/adapter, vue/guides/custom-plugins, solid/guides/custom-plugins (4) + +**Updated pages:** 5 +- overview, quick-start, installation, vite-plugin, framework/react/adapter + +**Moved pages:** 1 +- third-party-plugins (from Getting Started to Guides) + +**Unchanged pages:** ~15 +- configuration, plugin-configuration, production, all existing framework basic-setup/adapter pages, existing custom-plugins guides, API reference, examples From 218fd0e616df69de7ec86b727ed099747c1d8195 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 11:46:26 +0100 Subject: [PATCH 02/21] docs: add documentation implementation plan 18-task plan covering: navigation restructure, 4 new Concepts pages (architecture, event system, plugin lifecycle, source inspector), 3 new Guides (custom plugins, devtools-utils, bidirectional comms), Vue framework support, Solid custom plugins, and updates to existing pages including overview, quick-start, installation, and React adapter. --- ...2026-03-04-documentation-implementation.md | 814 ++++++++++++++++++ 1 file changed, 814 insertions(+) create mode 100644 docs/plans/2026-03-04-documentation-implementation.md diff --git a/docs/plans/2026-03-04-documentation-implementation.md b/docs/plans/2026-03-04-documentation-implementation.md new file mode 100644 index 00000000..70a36623 --- /dev/null +++ b/docs/plans/2026-03-04-documentation-implementation.md @@ -0,0 +1,814 @@ +# Documentation Redesign Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Rewrite and expand TanStack Devtools documentation with architecture concepts, Vue support, custom plugin guides, and restructured navigation. + +**Architecture:** Layered docs approach — Getting Started (setup), Concepts (architecture/events/plugins), Guides (building plugins), API Reference, Examples. All files in `docs/` with TanStack docs config.json navigation. + +**Tech Stack:** Markdown with YAML frontmatter, TanStack docs config.json schema + +--- + +### Task 1: Update config.json with new navigation structure + +**Files:** +- Modify: `docs/config.json` + +**Step 1: Replace config.json with the new navigation structure** + +```json +{ + "$schema": "https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json", + "docSearch": { + "appId": "", + "apiKey": "", + "indexName": "tanstack-devtools" + }, + "sections": [ + { + "label": "Getting Started", + "children": [ + { "label": "Overview", "to": "overview" }, + { "label": "Quick Start", "to": "quick-start" }, + { "label": "Installation", "to": "installation" }, + { "label": "Configuration", "to": "configuration" }, + { "label": "Plugin Configuration", "to": "plugin-configuration" }, + { "label": "Vite Plugin", "to": "vite-plugin" }, + { "label": "Production", "to": "production" } + ], + "frameworks": [ + { + "label": "react", + "children": [ + { "label": "Basic Setup", "to": "framework/react/basic-setup" }, + { "label": "React Adapter", "to": "framework/react/adapter" } + ] + }, + { + "label": "preact", + "children": [ + { "label": "Basic Setup", "to": "framework/preact/basic-setup" }, + { "label": "Preact Adapter", "to": "framework/preact/adapter" } + ] + }, + { + "label": "solid", + "children": [ + { "label": "Basic Setup", "to": "framework/solid/basic-setup" }, + { "label": "Solid Adapter", "to": "framework/solid/adapter" } + ] + }, + { + "label": "vue", + "children": [ + { "label": "Basic Setup", "to": "framework/vue/basic-setup" }, + { "label": "Vue Adapter", "to": "framework/vue/adapter" } + ] + } + ] + }, + { + "label": "Concepts", + "children": [ + { "label": "Architecture Overview", "to": "architecture" }, + { "label": "Event System", "to": "event-system" }, + { "label": "Plugin Lifecycle", "to": "plugin-lifecycle" }, + { "label": "Source Inspector", "to": "source-inspector" } + ] + }, + { + "label": "Guides", + "children": [ + { "label": "Building Custom Plugins", "to": "building-custom-plugins" }, + { "label": "Using devtools-utils", "to": "devtools-utils" }, + { "label": "Bidirectional Communication", "to": "bidirectional-communication" }, + { "label": "Third-party Plugins", "to": "third-party-plugins" } + ], + "frameworks": [ + { + "label": "react", + "children": [ + { "label": "Custom Plugins", "to": "framework/react/guides/custom-plugins" } + ] + }, + { + "label": "preact", + "children": [ + { "label": "Custom Plugins", "to": "framework/preact/guides/custom-plugins" } + ] + }, + { + "label": "solid", + "children": [ + { "label": "Custom Plugins", "to": "framework/solid/guides/custom-plugins" } + ] + }, + { + "label": "vue", + "children": [ + { "label": "Custom Plugins", "to": "framework/vue/guides/custom-plugins" } + ] + } + ] + }, + { + "label": "API Reference", + "children": [ + { "label": "Core API Reference", "to": "reference/index" } + ], + "frameworks": [ + { + "label": "react", + "children": [ + { "label": "React Reference", "to": "framework/react/reference/index" } + ] + }, + { + "label": "preact", + "children": [ + { "label": "Preact Reference", "to": "framework/preact/reference/index" } + ] + }, + { + "label": "solid", + "children": [ + { "label": "Solid Reference", "to": "framework/solid/reference/index" } + ] + }, + { + "label": "vue", + "children": [ + { "label": "Vue Reference", "to": "framework/vue/reference/index" } + ] + } + ] + }, + { + "label": "Examples", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { "label": "Basic", "to": "framework/react/examples/basic" }, + { "label": "TanStack Start", "to": "framework/react/examples/start" }, + { "label": "Custom Devtools", "to": "framework/react/examples/custom-devtools" } + ] + }, + { + "label": "preact", + "children": [ + { "label": "Basic", "to": "framework/preact/examples/basic" }, + { "label": "Custom Devtools", "to": "framework/preact/examples/custom-devtools" } + ] + }, + { + "label": "solid", + "children": [ + { "label": "Basic", "to": "framework/solid/examples/basic" } + ] + }, + { + "label": "vue", + "children": [ + { "label": "Basic", "to": "framework/vue/examples/basic" } + ] + } + ] + } + ] +} +``` + +**Step 2: Commit** + +```bash +git add docs/config.json +git commit -m "docs: restructure navigation with Concepts and Guides sections, add Vue" +``` + +--- + +### Task 2: Rewrite Overview page + +**Files:** +- Modify: `docs/overview.md` + +**Step 1: Rewrite overview.md with package map and architecture diagram** + +The updated overview should contain: +1. Keep the existing intro paragraph and origin story +2. Add a "What's in the Box" section listing all packages grouped by layer: + - **Framework Adapters**: `@tanstack/react-devtools`, `@tanstack/vue-devtools`, `@tanstack/solid-devtools`, `@tanstack/preact-devtools` + - **Core**: `@tanstack/devtools` — the shell UI, plugin system, settings + - **Event System**: `@tanstack/devtools-event-client` — typed event client for plugins; `@tanstack/devtools-event-bus` — WebSocket/SSE transport + - **Build Tools**: `@tanstack/devtools-vite` — Vite plugin for source inspection, console piping, production stripping + - **Utilities**: `@tanstack/devtools-utils` — plugin factory helpers; `@tanstack/devtools-ui` — shared UI components; `@tanstack/devtools-client` — core devtools events +3. Add a text-based architecture diagram showing the 3 layers +4. Update the Key Features list to include: Framework Agnostic, Plugin Marketplace, Type-Safe Event System, Source Inspector, Console Piping, Picture-in-Picture mode +5. Add "Next Steps" links to Quick Start and Architecture Overview + +**Step 2: Commit** + +```bash +git add docs/overview.md +git commit -m "docs: expand overview with package map and architecture diagram" +``` + +--- + +### Task 3: Update Quick Start to cover all frameworks + +**Files:** +- Modify: `docs/quick-start.md` + +**Step 1: Expand quick-start.md with all 4 frameworks** + +Add install commands and code examples for all 4 frameworks. The existing React content stays. Add: + +- **Preact** section: `npm install @tanstack/preact-devtools @tanstack/devtools-vite`, with `import { TanStackDevtools } from '@tanstack/preact-devtools'` and Preact render pattern +- **Solid** section: `npm install @tanstack/solid-devtools @tanstack/devtools-vite`, with Solid render pattern +- **Vue** section: `npm install @tanstack/vue-devtools`, with ` + + + ``` +3. With plugins (Vue Query example): + ```vue + + + + ``` +4. Note the key difference: Vue uses `component` instead of `render` in plugin definitions. This is because Vue components are passed as component references, not JSX elements. +5. Link to configuration docs and the Vue basic example. + +**Step 2: Commit** + +```bash +git add docs/framework/vue/basic-setup.md +git commit -m "docs: add Vue basic setup page" +``` + +--- + +### Task 13: Create Vue Adapter page + +**Files:** +- Create: `docs/framework/vue/adapter.md` + +**Step 1: Write adapter.md** + +Frontmatter: `title: TanStack Devtools Vue Adapter`, `id: adapter` + +Content: +1. **Overview** — The Vue adapter wraps `TanStackDevtoolsCore` in a Vue 3 component. It uses Vue's `` to render plugin components into the devtools' DOM containers. +2. **Installation** — `npm install @tanstack/vue-devtools` +3. **Component Props** — `TanStackDevtoolsVueInit` interface: + - `plugins?: TanStackDevtoolsVuePlugin[]` — Array of plugins + - `config?: Partial` — Devtools configuration + - `eventBusConfig?: ClientEventBusConfig` — Event bus configuration +4. **Plugin Type** — `TanStackDevtoolsVuePlugin`: + - `id?: string` — Unique identifier + - `component: Component` — Vue component to render in the panel + - `name: string | Component` — Display name or Vue component for the tab title + - `props?: Record` — Additional props passed to the component +5. **Key Difference from Other Adapters** — Vue plugins use `component` (a Vue component reference) instead of `render` (a JSX element). Props are passed via the `props` field and are bound with `v-bind`. +6. **Exports** — `TanStackDevtools` (component), `TanStackDevtoolsVuePlugin` (type), `TanStackDevtoolsVueInit` (type). Also re-exports everything from `@tanstack/devtools`. + +**Step 2: Commit** + +```bash +git add docs/framework/vue/adapter.md +git commit -m "docs: add Vue adapter page" +``` + +--- + +### Task 14: Create Vue Custom Plugins guide + +**Files:** +- Create: `docs/framework/vue/guides/custom-plugins.md` + +**Step 1: Write custom-plugins.md** + +Frontmatter: `title: Custom plugins`, `id: custom-plugins` + +Mirrors the React custom-plugins guide but with Vue syntax. Same counter example. + +Key differences: +- DevtoolsPanel is a Vue component using ` + + +``` + +To add plugins, define them as an array and pass them via the `:plugins` binding. Vue uses `component` instead of `render` in plugin definitions: + +```vue + + + +``` + +## Vite Plugin + +All frameworks benefit from the optional Vite plugin, which provides enhanced console logs, go-to-source, and a server event bus. Install it as a dev dependency: + +```bash +npm install -D @tanstack/devtools-vite +``` + +Add it as the **first** plugin in your Vite config: + +```ts +import { devtools } from '@tanstack/devtools-vite' + +export default { + plugins: [ + devtools(), + // ... rest of your plugins here + ], } -``` \ No newline at end of file +``` + +For the full list of Vite plugin options, see the [Vite Plugin](./vite-plugin.md) documentation. From 08fda0496c084bd4cf948e67f718b66da1554ff3 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:02:30 +0100 Subject: [PATCH 06/21] docs: add Vue and Preact installation instructions --- docs/installation.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index f1535529..f65231e0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -16,6 +16,15 @@ npm install -D @tanstack/devtools-vite TanStack Devtools is compatible with React v16.8+ +## Preact + +```sh +npm install -D @tanstack/preact-devtools +npm install -D @tanstack/devtools-vite +``` + +TanStack Devtools is compatible with Preact v10+ + ## Solid ```sh @@ -25,6 +34,20 @@ npm install -D @tanstack/devtools-vite TanStack Devtools is compatible with Solid v1.9.5+ +## Vue + +```sh +npm install -D @tanstack/vue-devtools +``` + +The Vite plugin (`@tanstack/devtools-vite`) is optional for Vue — it enables additional features like source inspection and console piping but isn't required for basic usage. + +```sh +npm install -D @tanstack/devtools-vite +``` + +TanStack Devtools is compatible with Vue 3+ + ## Vanilla JS ```sh From 6af06960b8a8ba7220b7de8938579e67b7e0a3a0 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:02:46 +0100 Subject: [PATCH 07/21] docs: add architecture overview page --- docs/architecture.md | 191 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 docs/architecture.md diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..5c9e7b39 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,191 @@ +--- +title: Architecture Overview +id: architecture +--- + +TanStack Devtools is a modular system of packages organized into three layers: **Framework Adapters**, **Core Shell**, and **Event Transport**. This architecture lets you use pre-built devtools panels or build your own custom ones, regardless of which frontend framework you use. + +## Package Dependency Graph + +``` +Framework Adapters (react-devtools, vue-devtools, solid-devtools, preact-devtools) + └── @tanstack/devtools (core shell, built in Solid.js) + ├── @tanstack/devtools-client (core devtools events) + │ └── @tanstack/devtools-event-client (generic EventClient) + ├── @tanstack/devtools-ui (shared UI components) + └── @tanstack/devtools-event-bus/client (ClientEventBus) + +Build Tools: + @tanstack/devtools-vite + ├── @tanstack/devtools-client + └── @tanstack/devtools-event-bus/server (ServerEventBus) + +Utilities: + @tanstack/devtools-utils + └── @tanstack/devtools-ui +``` + +Each framework adapter depends only on `@tanstack/devtools`. The core shell pulls in everything it needs, so end users install just two packages: their framework adapter and the Vite plugin. + +## Transport Layer + +The transport layer handles event delivery between plugins, the devtools UI, and (optionally) a dev server. It is composed of three pieces. + +### ServerEventBus (`@tanstack/devtools-event-bus/server`) + +Runs inside the Vite dev server process (Node.js). It creates an HTTP server (or piggybacks on Vite's existing server when HTTPS is enabled) that accepts both **WebSocket** and **SSE** connections. When a message arrives from any client, the server broadcasts it to every other connected client and dispatches it on a server-side `EventTarget` so server-side listeners (like the Vite plugin's package-manager helpers) can react to it. + +Key details: +- Default port is `4206`, auto-increments if the port is in use. +- Handles `/__devtools/ws` for WebSocket upgrades, `/__devtools/sse` for SSE streams, and `/__devtools/send` for SSE POST fallback. +- Sets `globalThis.__TANSTACK_EVENT_TARGET__` so that `EventClient` instances running on the server can dispatch events onto the same target. + +### ClientEventBus (`@tanstack/devtools-event-bus/client`) + +Runs in the browser. Started automatically when the core shell mounts via `TanStackDevtoolsCore.mount()`. Its responsibilities: + +1. **Local dispatch** -- Listens for `tanstack-dispatch-event` CustomEvents on `window`, re-dispatches them as both a type-specific CustomEvent (e.g. `my-plugin:state-update`) and a global `tanstack-devtools-global` event so listeners can subscribe to individual event types or to all events. +2. **Server forwarding** -- If connected to a server bus, forwards every dispatched event over WebSocket (preferred) or SSE POST fallback. +3. **Cross-tab sync** -- Uses `BroadcastChannel('tanstack-devtools')` to replicate events across browser tabs without round-tripping through the server. +4. **Connection handshake** -- Responds to `tanstack-connect` events with `tanstack-connect-success`, allowing `EventClient` instances to discover the bus. + +### EventClient (`@tanstack/devtools-event-client`) + +The high-level, typed API that plugins use to send and receive events. Each `EventClient` is created with a `pluginId` and a type map that defines the events it can emit and listen to. + +```ts +import { EventClient } from '@tanstack/devtools-event-client' + +type MyEvents = { + 'state-update': { count: number } + 'reset': void +} + +const client = new EventClient({ pluginId: 'my-plugin' }) +``` + +When you call `client.emit('state-update', { count: 42 })`, the EventClient: + +1. Dispatches a CustomEvent on its internal `EventTarget` (for same-page listeners using the `withEventTarget` option). +2. Dispatches a `tanstack-dispatch-event` CustomEvent on the global target (typically `window`), with a payload of `{ type: 'my-plugin:state-update', payload: { count: 42 }, pluginId: 'my-plugin' }`. +3. The `ClientEventBus` picks up that `tanstack-dispatch-event`, re-dispatches it as a `my-plugin:state-update` CustomEvent on `window`, and forwards it to the server bus via WebSocket. + +When you call `client.on('state-update', callback)`, the EventClient registers a listener on the global target for `my-plugin:state-update` events, so it receives events regardless of whether they came from a local emit or from the server bus. + +> [!NOTE] +> The server bus is optional. Without the Vite plugin, `EventClient` still works for same-page communication via CustomEvent dispatch on `window`. Events simply won't cross tab or process boundaries. + +### Event Flow Summary + +``` +EventClient.emit() + → CustomEvent('tanstack-dispatch-event') on window + → ClientEventBus picks it up + ├── Re-dispatches as CustomEvent('my-plugin:state-update') on window + │ → EventClient.on() callbacks fire + ├── BroadcastChannel → other tabs receive the event + └── WebSocket → ServerEventBus + → broadcasts to all connected clients + → events arrive in other browser clients + → EventClient.on() callbacks fire there too +``` + +## Core Layer + +### @tanstack/devtools -- The Shell + +The devtools shell is a Solid.js application that renders the entire devtools UI. It exposes the `TanStackDevtoolsCore` class with three methods: + +- **`mount(el)`** -- Renders the Solid.js devtools application into the given DOM element. Starts a `ClientEventBus` and lazy-loads the UI. Wraps everything in a `DevtoolsProvider` (reactive store for plugins, settings, state) and a `PiPProvider` (Picture-in-Picture support). +- **`unmount()`** -- Tears down the Solid.js app and stops the event bus. +- **`setConfig(config)`** -- Updates configuration and plugins at runtime. Plugins are reactive: adding or removing them updates the tab bar immediately. + +The shell renders: +- A **trigger button** (the floating devtools toggle, customizable or replaceable) +- A **resizable panel** (docked to the bottom of the viewport, resizable via drag) +- **Tab navigation** for switching between plugins, settings, SEO inspector, and the plugin marketplace +- A **settings panel** for theme, hotkeys, position, and other preferences +- **Plugin containers** -- DOM elements where each plugin's UI is mounted + +Settings and UI state (panel size, position, active tab, theme) are persisted in `localStorage` so they survive page reloads. + +### @tanstack/devtools-ui -- Component Library + +A shared Solid.js component library used by the core shell and available for use in Solid.js plugins. Provides buttons, inputs, checkboxes, a JSON tree viewer, section layouts, and other UI primitives. The `@tanstack/devtools-utils` package also depends on it to provide framework-specific plugin helpers. + +### @tanstack/devtools-client -- Core Event Client + +A specialized `EventClient` pre-configured with `pluginId: 'tanstack-devtools-core'` and a fixed event map for devtools-internal operations: + +- `mounted` -- Fired when the devtools UI has mounted, triggers the server to send current package.json and outdated dependency data. +- `package-json-read` / `outdated-deps-read` -- Carries project metadata from the Vite server to the devtools UI. +- `install-devtools` / `devtools-installed` -- Request/response cycle for installing a plugin package from the marketplace. +- `add-plugin-to-devtools` / `plugin-added` -- Request/response cycle for injecting a plugin into the user's source code. +- `trigger-toggled` -- Synchronizes the open/closed state of the devtools panel. + +This client is a singleton (`devtoolsEventClient`) used by both the core shell and the Vite plugin to coordinate. + +## Framework Layer + +Each framework adapter is a thin wrapper that bridges its framework's component model to the core Solid.js shell. The pattern is the same across all adapters: + +1. **Creates a `TanStackDevtoolsCore` instance** with the user's plugins and config. +2. **Mounts it to a DOM element** using the framework's lifecycle hooks (`useEffect` in React, `onMounted` in Vue, `onMount` in Solid). +3. **Converts framework-specific plugin definitions** into the core's DOM-based `render(el, theme)` interface. Each adapter defines its own plugin type (e.g. `TanStackDevtoolsReactPlugin`) that accepts framework-native components, then wraps them in a `render` callback that the core calls with a target DOM element and the current theme. +4. **Uses the framework's portal/teleport mechanism** to render plugin components into the core's DOM containers: + - **React** -- `createPortal()` from `react-dom` + - **Vue** -- `` + - **Solid** -- `` + - **Preact** -- Same portal pattern as React + +The key insight: the core shell is always Solid.js, but your plugins run in **your** framework. A React plugin is a real React component rendered by React's `createPortal` into a DOM element that the Solid.js shell created. A Vue plugin is a real Vue component rendered by Vue's ``. The adapters bridge this gap so you never need to think about Solid.js unless you want to. + +### What an adapter does NOT do + +Adapters do not re-implement the devtools UI, manage settings, handle events, or communicate with the server. All of that lives in the core shell. Adapters are intentionally minimal -- typically a single file under 300 lines. + +## Build Layer + +`@tanstack/devtools-vite` is a collection of Vite plugins that enhance the development experience and clean up production builds. It returns an array of Vite plugins, each handling a specific concern: + +### Source injection (`@tanstack/devtools:inject-source`) +Uses Babel to parse JSX/TSX files and injects `data-tsd-source` attributes on every JSX element. These attributes encode the file path, line number, and column number of each element in source code, which the source inspector feature uses to implement click-to-open-in-editor. + +### Server event bus (`@tanstack/devtools:custom-server`) +Starts a `ServerEventBus` on the Vite dev server. Also sets up middleware for the go-to-source editor integration and bidirectional console piping (client logs appear in the terminal, server logs appear in the browser). + +### Production stripping (`@tanstack/devtools:remove-devtools-on-build`) +On production builds, transforms any file that imports from `@tanstack/*-devtools` to remove the devtools imports and JSX usage entirely. This means devtools add zero bytes to your production bundle. + +### Console piping (`@tanstack/devtools:console-pipe-transform`) +Injects a small runtime into your application's entry file that intercepts `console.log/warn/error/info/debug` calls and forwards them to the Vite dev server via HTTP POST. The server then broadcasts them to connected SSE clients, enabling server-to-browser log forwarding. + +### Enhanced logging (`@tanstack/devtools:better-console-logs`) +Transforms `console.*` calls to prepend source location information (file, line, column), making it possible to click a console log and jump directly to the source. + +### Plugin marketplace support (`@tanstack/devtools:event-client-setup`) +Listens for `install-devtools` events from the devtools UI, runs the package manager to install the requested package, and then uses AST manipulation to inject the plugin import and configuration into the user's source code. + +### Connection injection (`@tanstack/devtools:connection-injection`) +Replaces compile-time placeholders (`__TANSTACK_DEVTOOLS_PORT__`, `__TANSTACK_DEVTOOLS_HOST__`, `__TANSTACK_DEVTOOLS_PROTOCOL__`) in the event bus client code with the actual values from the running dev server, so the client automatically connects to the correct server. + +## Data Flow + +To tie everything together, here is what happens when a plugin emits an event end-to-end: + +1. **Your library code** calls `eventClient.emit('state-update', data)`. + +2. **EventClient** constructs a payload `{ type: 'my-plugin:state-update', payload: data, pluginId: 'my-plugin' }` and dispatches it as a `tanstack-dispatch-event` CustomEvent on `window`. + +3. **ClientEventBus** receives the `tanstack-dispatch-event`. It does three things: + - Dispatches a CustomEvent named `my-plugin:state-update` on `window` so that any `eventClient.on('state-update', callback)` listeners on this page fire immediately. + - Dispatches a `tanstack-devtools-global` CustomEvent on `window` so that `onAll()` and `onAllPluginEvents()` listeners fire. + - Posts the event to the `BroadcastChannel` so other tabs receive it. + +4. **If connected to the server bus**, ClientEventBus also sends the event over WebSocket to `ServerEventBus`. + +5. **ServerEventBus** receives the WebSocket message and broadcasts it to all other connected clients (WebSocket and SSE). It also dispatches the event on its server-side `EventTarget` so server-side listeners (e.g., the Vite plugin) can react. + +6. **In other browser tabs/windows**, the event arrives via WebSocket from the server (or via BroadcastChannel from step 3). The local `ClientEventBus` dispatches it as a `my-plugin:state-update` CustomEvent, and any `eventClient.on('state-update', callback)` listeners fire with the data. + +Without the Vite plugin and server bus, steps 4-6 are skipped, but steps 1-3 still work. This means plugins can communicate within a single page without any server infrastructure -- the server bus just adds cross-tab and cross-process capabilities. From 6b465dc28b62b0e238636c8d73e2bd49256ca2ba Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:03:07 +0100 Subject: [PATCH 08/21] docs: add event system concepts page --- docs/event-system.md | 158 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 docs/event-system.md diff --git a/docs/event-system.md b/docs/event-system.md new file mode 100644 index 00000000..d440e710 --- /dev/null +++ b/docs/event-system.md @@ -0,0 +1,158 @@ +--- +title: Event System +id: event-system +--- + +The event system is how plugins communicate with the devtools UI and with the application. It is built on `EventClient`, a type-safe event emitter/listener from `@tanstack/devtools-event-client`. It is completely framework-agnostic. + +## EventClient Basics + +Create a typed `EventClient` by extending the base class with your event map: + +```ts +import { EventClient } from '@tanstack/devtools-event-client' + +type MyEvents = { + 'my-plugin:state-update': { count: number } + 'my-plugin:action': { type: string } +} + +class MyEventClient extends EventClient { + constructor() { + super({ pluginId: 'my-plugin' }) + } +} + +export const myEventClient = new MyEventClient() +``` + +The constructor accepts the following options: + +| Option | Type | Default | Description | +| ------------------ | --------- | ------- | ---------------------------------------------------- | +| `pluginId` | `string` | — | Required. Identifies this plugin in the event system. | +| `debug` | `boolean` | `false` | Enable debug logging to the console. | +| `enabled` | `boolean` | `true` | Whether the client connects to the bus at all. | +| `reconnectEveryMs` | `number` | `300` | Interval (ms) between connection retry attempts. | + +## Event Maps and Type Safety + +The generic `EventMap` type maps event names to payload types. Keys follow the `{pluginId}:{eventSuffix}` convention: + +```ts +type MyEvents = { + 'my-plugin:state-update': { count: number } + 'my-plugin:action': { type: string } +} +``` + +TypeScript enforces correct event names and payload shapes at compile time. You get autocomplete on event names and type errors if the payload does not match the declared shape. + +## Emitting Events + +Call `eventClient.emit(eventSuffix, payload)` to dispatch an event. You pass only the **suffix** (the part after the colon). The `pluginId` is prepended automatically: + +```ts +// If pluginId is 'my-plugin' and event map has 'my-plugin:state-update' +myEventClient.emit('state-update', { count: 42 }) +// Dispatches event named 'my-plugin:state-update' +``` + +If the client is not yet connected to the bus, the event is queued and flushed once the connection succeeds (see [Connection Lifecycle](#connection-lifecycle) below). + +## Listening to Events + +There are three methods for subscribing to events. Each returns a cleanup function you call to unsubscribe. + +### `on(eventSuffix, callback)` + +Listen to a specific event from this plugin. Like `emit`, you pass only the suffix: + +```ts +const cleanup = myEventClient.on('state-update', (event) => { + console.log(event.payload.count) // typed as { count: number } +}) + +// Later: stop listening +cleanup() +``` + +The callback receives the full event object: + +```ts +{ + type: 'my-plugin:state-update', // fully qualified event name + payload: { count: number }, // typed payload + pluginId: 'my-plugin' // originating plugin +} +``` + +### `onAll(callback)` + +Listen to **all** events from **all** plugins. Useful for logging, debugging, or building cross-plugin features: + +```ts +const cleanup = myEventClient.onAll((event) => { + console.log(event.type, event.payload) +}) +``` + +### `onAllPluginEvents(callback)` + +Listen to all events from **this** plugin only (filtered by `pluginId`): + +```ts +const cleanup = myEventClient.onAllPluginEvents((event) => { + // Only fires for events where event.pluginId === 'my-plugin' + console.log(event.type, event.payload) +}) +``` + +## Connection Lifecycle + +The `EventClient` manages its connection to the event bus automatically: + +1. **Queueing** — When you call `emit()` before the client is connected, events are queued in memory. +2. **Connection** — On the first `emit()`, the client dispatches a `tanstack-connect` event and starts a retry loop. +3. **Retries** — The client retries every `reconnectEveryMs` (default: 300ms) up to a maximum of 5 attempts. +4. **Flush** — Once a `tanstack-connect-success` event is received, all queued events are flushed to the bus in order. +5. **Failure** — If all 5 retries are exhausted without a successful connection, the client stops retrying. Subsequent `emit()` calls are silently dropped (they will not be queued). + +### The `enabled` Option + +When `enabled` is set to `false` in the constructor, the `EventClient` does not connect to the bus at all. Events are only dispatched internally via `EventTarget`, which is useful for components on the same page that do not need server communication. All `on`, `onAll`, and `onAllPluginEvents` calls return no-op cleanup functions when the client is disabled. + +## Server Event Bus + +When `connectToServerBus: true` is set in the component's `eventBusConfig` prop, the `ClientEventBus` connects to the `ServerEventBus` started by the Vite plugin (default port 4206). This enables server-side features like console piping and the plugin marketplace. + +```tsx + +``` + +Without the Vite plugin running, the `EventClient` still works for same-page communication between your application code and the devtools panel. The server bus is only needed for features that bridge the browser and the dev server. + +## Debugging + +Set `debug: true` in the `EventClient` constructor or in `eventBusConfig` to enable verbose console logging. Debug logs are prefixed with `[tanstack-devtools:{pluginId}]` for plugin events and `[tanstack-devtools:client-bus]` for the client bus. + +```ts +const myEventClient = new MyEventClient() +// In the constructor: super({ pluginId: 'my-plugin', debug: true }) +``` + +Example output: + +``` +🌴 [tanstack-devtools:client-bus] Initializing client event bus +🌴 [tanstack-devtools:my-plugin] Registered event to bus my-plugin:state-update +🌴 [tanstack-devtools:my-plugin] Emitting event my-plugin:state-update +``` + +This is helpful when diagnosing issues with event delivery, connection timing, or verifying that your event map is wired up correctly. From f68d56f5962c3a1f6e48cafeacf507c3c6e1b42e Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:03:28 +0100 Subject: [PATCH 09/21] docs: add plugin lifecycle concepts page --- docs/plugin-lifecycle.md | 205 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 docs/plugin-lifecycle.md diff --git a/docs/plugin-lifecycle.md b/docs/plugin-lifecycle.md new file mode 100644 index 00000000..b26fd9f5 --- /dev/null +++ b/docs/plugin-lifecycle.md @@ -0,0 +1,205 @@ +--- +title: Plugin Lifecycle +id: plugin-lifecycle +--- + +Every TanStack Devtools plugin follows a well-defined lifecycle: it is registered, mounted into a DOM container, rendered when activated, and cleaned up when the devtools unmount. This page walks through each stage in detail. + +## Plugin Interface + +All plugins implement the `TanStackDevtoolsPlugin` interface, which is the low-level contract between a plugin and the devtools core. Framework adapters (React, Vue, Solid, Preact) wrap this interface so you can work with familiar components, but under the hood every plugin is reduced to these fields: + +```ts +interface TanStackDevtoolsPlugin { + id?: string + name: string | ((el: HTMLHeadingElement, theme: 'dark' | 'light') => void) + render: (el: HTMLDivElement, theme: 'dark' | 'light') => void + destroy?: (pluginId: string) => void + defaultOpen?: boolean +} +``` + +### `id` (optional) + +A unique identifier for the plugin. If you omit it, the core generates one from the `name` string (lowercased, spaces replaced with dashes, suffixed with the plugin's index). Providing an explicit `id` is useful when you need a stable identifier across page reloads - for example, to persist which plugins the user had open. + +### `name` (required) + +Displayed as the tab title in the sidebar. This can be: + +- **A plain string** - rendered as text inside an `

` element. +- **A function** - receives the heading element (`HTMLHeadingElement`) and the current theme (`'dark' | 'light'`). You can render anything into the heading, such as an icon next to the name or a fully custom title. + +```ts +// Simple string name +{ name: 'My Plugin', render: (el) => { /* ... */ } } + +// Custom title via function +{ + name: (el, theme) => { + el.innerHTML = `My Plugin` + }, + render: (el) => { /* ... */ } +} +``` + +### `render` (required) + +The main rendering function. It receives a `
` container element and the current theme. Your job is to render your plugin UI into this container using whatever approach you prefer - raw DOM manipulation, a framework portal, or anything else. + +```ts +render: (el, theme) => { + el.innerHTML = `
Hello from my plugin!
` +} +``` + +The `render` function is called: + +1. When the plugin's tab is first activated (clicked or opened by default). +2. When the theme changes, so your UI can adapt. + +### `destroy` (optional) + +Called when the plugin is removed from the active set (e.g., the user deactivates the tab) or when the devtools unmount entirely. It receives the `pluginId` as its argument. Use this for cleanup - cancelling timers, closing WebSocket connections, removing event listeners, etc. + +Most plugins don't need to implement `destroy` because framework adapters handle cleanup automatically. + +### `defaultOpen` (optional) + +When set to `true`, the plugin's panel will open automatically when the devtools first load and no user preferences exist in localStorage. At most 3 plugins can be open simultaneously, so if more than 3 specify `defaultOpen: true`, only the first 3 are opened. + +This setting does **not** override saved user preferences. Once a user has interacted with the devtools and their active-plugin selection is persisted, `defaultOpen` has no effect. + +If only a single plugin is registered, it opens automatically regardless of `defaultOpen`. + +## Mount Sequence + +Here is what happens when you provide plugins to the devtools: + +1. **Initialization** - `TanStackDevtoolsCore` is instantiated with a `plugins` array. Each plugin is assigned an `id` if one is not already provided. + +2. **DOM containers are created** - The core's Solid-based UI creates two DOM elements per plugin: + - A content container: `
` where the plugin renders its UI. + - A title container: `

` where the plugin's name is rendered. + +3. **Title rendering** - For each plugin, the core checks if `name` is a string or function. If it's a string, the text is set directly on the heading element. If it's a function, the function is called with the heading element and current theme. + +4. **Plugin activation** - When the user clicks a plugin's tab (or the plugin is auto-opened via `defaultOpen`), the plugin is added to the `activePlugins` list. The core then calls `plugin.render(container, theme)` with the content `
` and the current theme. + +5. **Rendering** - The container is a regular `
` element. Your plugin can render anything into it - DOM nodes, a framework component tree via portals, a canvas, an iframe, etc. + +6. **Theme changes** - When the user toggles the theme in settings, `render` is called again with the new theme value. Your plugin should update its appearance accordingly. + +``` +User opens devtools + └─ TanStackDevtoolsCore created with plugins array + └─ Solid UI renders sidebar tabs and containers + ├─

created per plugin + │ └─ plugin.name (string set or function called) + └─
created per active plugin + └─ plugin.render(div, theme) called +``` + +## Framework Adapter Pattern + +You rarely interact with the raw `TanStackDevtoolsPlugin` interface directly. Instead, each framework adapter converts your familiar component model into the DOM-based plugin contract. + +### React / Preact + +The React adapter takes your JSX element and uses `createPortal` to render it into the plugin's container element: + +```tsx +// What you write: +, + }]} +/> + +// What happens internally: +// The adapter's render function calls: +// createPortal(, containerElement) +``` + +Your React component runs in its normal React tree with full access to hooks, context, state management, etc. It just renders into a different DOM location via the portal. + +### Solid + +The Solid adapter uses Solid's `` component to mount your JSX into the container: + +```tsx +// What you write: +, + }]} +/> + +// What happens internally: +// The adapter wraps your component in: +// {yourComponent} +``` + +Since the devtools core is itself built in Solid, this is the most native integration. Your component runs inside the same Solid reactive system as the devtools shell. + +### Vue + +The Vue adapter uses `` to render your Vue component into the container: + +```vue + + + + + + + + +``` + +Your Vue component receives the `theme` as a prop along with any other props you pass. It runs within the Vue app's reactivity system with full access to composition API, inject/provide, etc. + +### The Key Insight + +Regardless of framework, your plugin component runs in its **normal framework context** with full reactivity, hooks, signals, lifecycle methods, and dependency injection. It just renders into a different DOM location via portals or teleports. This means: + +- React plugins can use `useState`, `useEffect`, `useContext`, and any React library. +- Solid plugins can use signals, stores, `createEffect`, and the full Solid API. +- Vue plugins can use `ref`, `computed`, `watch`, `inject`, and any Vue composable. + +The framework adapter handles all the wiring between your component and the devtools container. + +## Plugin State + +The devtools persist the active/visible plugin selection in `localStorage` under the key `tanstack_devtools_state`. This means that when a user opens specific plugin tabs, their selection survives page reloads. + +Key behaviors: + +- **Maximum 3 visible plugins** - At most 3 plugin panels can be displayed simultaneously. Activating a 4th plugin deactivates the earliest one. +- **`defaultOpen` vs. saved state** - If `defaultOpen: true` is set on a plugin and no saved state exists in localStorage, the plugin opens on first load. Once the user changes the selection, their preference takes precedence. +- **Single-plugin auto-open** - If only one plugin is registered, it opens automatically regardless of `defaultOpen` or saved state. +- **Stale plugin cleanup** - When the devtools load, any plugin IDs in the saved state that no longer match a registered plugin are automatically removed. + +## Cleanup + +When `TanStackDevtoolsCore.unmount()` is called - either explicitly or because the framework component unmounts - the following happens: + +1. The Solid reactive system disposes of the entire devtools component tree. +2. For each active plugin that provides a `destroy` function, `destroy(pluginId)` is called. +3. All DOM containers created by the core are removed. + +Framework adapters handle their own cleanup automatically: + +- **React** unmounts the portal, which triggers the normal React cleanup cycle (`useEffect` cleanup functions, etc.). +- **Vue** destroys the Teleport, which runs `onUnmounted` hooks in your component. +- **Solid** disposes of the Portal's reactive scope, running any `onCleanup` callbacks. + +You typically do **not** need to implement `destroy` unless your plugin has manual subscriptions, timers, WebSocket connections, or other resources that aren't tied to your framework's lifecycle. If all your cleanup is handled by framework hooks (like `useEffect` cleanup in React or `onCleanup` in Solid), the adapter takes care of it for you. From 6053a2e704ded0cfe7236c9efc130c06d39c2b8f Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:03:44 +0100 Subject: [PATCH 10/21] docs: add source inspector concepts page --- docs/source-inspector.md | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/source-inspector.md diff --git a/docs/source-inspector.md b/docs/source-inspector.md new file mode 100644 index 00000000..ff73b325 --- /dev/null +++ b/docs/source-inspector.md @@ -0,0 +1,78 @@ +--- +title: Source Inspector +id: source-inspector +--- + +The source inspector lets you click any element in your app to open its source file in your editor. When activated, the devtools overlay highlights elements as you hover over them and shows their source file location. Click to open the file at the exact line. + +## Requirements + +Two things are needed for the source inspector to work: + +- The `@tanstack/devtools-vite` plugin must be installed and running (dev server only) +- Source injection must be enabled: `injectSource.enabled: true` (this is the default) + +The feature only works in development. In production builds, source attributes are not injected. + +## How It Works + +The Vite plugin uses Babel to parse your JSX/TSX files during development. It adds a `data-tsd-source="filepath:line:column"` attribute to every JSX element. When you activate the source inspector and click an element, the devtools reads this attribute and sends a request to the Vite dev server. The server then launches your editor at the specified file and line using `launch-editor`. + +## Activating the Inspector + +There are two ways to activate the source inspector: + +- **Hotkey**: Hold Shift+Alt+Ctrl (or Shift+Alt+Meta on Mac) — this is the default `inspectHotkey`. While held, the inspector overlay appears. +- **Settings panel**: The inspect hotkey can be customized in the devtools Settings tab. + +The hotkey can also be configured programmatically: + +```ts + +``` + +## Ignoring Files and Components + +Not all elements need source attributes. Use the `ignore` config to exclude files or components: + +```ts +import { devtools } from '@tanstack/devtools-vite' + +export default { + plugins: [ + devtools({ + injectSource: { + enabled: true, + ignore: { + files: ['node_modules', /.*\.test\.(js|ts|jsx|tsx)$/], + components: ['InternalComponent', /.*Provider$/], + }, + }, + }), + ], +} +``` + +Both `files` and `components` accept arrays of strings (exact match) or RegExp patterns. + +## Editor Configuration + +Most popular editors work out of the box via the `launch-editor` package. Supported editors include VS Code, WebStorm, Sublime Text, Atom, and more ([full list](https://github.com/yyx990803/launch-editor?tab=readme-ov-file#supported-editors)). + +For unsupported editors, use the `editor` config: + +```ts +devtools({ + editor: { + name: 'My Editor', + open: async (path, lineNumber, columnNumber) => { + const { exec } = await import('node:child_process') + exec(`myeditor --goto "${path}:${lineNumber}:${columnNumber}"`) + }, + }, +}) +``` From 8467fc3f96e9ba40e79d59d0d8cc204ee9a34fd7 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:13:15 +0100 Subject: [PATCH 11/21] docs: add framework-agnostic custom plugin building guide --- docs/building-custom-plugins.md | 192 ++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 docs/building-custom-plugins.md diff --git a/docs/building-custom-plugins.md b/docs/building-custom-plugins.md new file mode 100644 index 00000000..5ddbbf59 --- /dev/null +++ b/docs/building-custom-plugins.md @@ -0,0 +1,192 @@ +--- +title: Building Custom Plugins +id: building-custom-plugins +--- + +You can build custom devtools plugins for any state management library, API client, or internal tool. A plugin consists of two parts: + +1. **An EventClient** — sends and receives data between your application code and the devtools panel. +2. **A panel component** — displays the data inside the devtools UI. + +The EventClient is framework-agnostic. It works the same in React, Vue, Solid, Preact, or vanilla JavaScript. The panel component can be written in any framework supported by an adapter. + +This guide walks through building a custom plugin from scratch using a "store inspector" as a running example. + +## Step 1: Define Your Event Map + +Start by creating a TypeScript type that maps event names to their payload types: + +```ts +type StoreEvents = { + 'store-inspector:state-changed': { storeName: string; state: unknown; timestamp: number } + 'store-inspector:action-dispatched': { storeName: string; action: string; payload: unknown } + 'store-inspector:reset': void +} +``` + +Every key in the event map follows the `{pluginId}:{suffix}` naming convention. The `pluginId` portion (`store-inspector` in this example) **must** match the `pluginId` you pass to the `EventClient` constructor in the next step. The suffix (`state-changed`, `action-dispatched`, `reset`) is whatever name makes sense for your event. + +The value of each key is the payload type. Use `void` for events that carry no data. + +## Step 2: Create an EventClient + +Extend the base `EventClient` class with your event map: + +```ts +import { EventClient } from '@tanstack/devtools-event-client' + +class StoreInspectorClient extends EventClient { + constructor() { + super({ pluginId: 'store-inspector' }) + } +} + +export const storeInspector = new StoreInspectorClient() +``` + +Install the event client package if you haven't already: + +```bash +npm i @tanstack/devtools-event-client +``` + +The constructor accepts additional options beyond `pluginId`: + +| Option | Type | Default | Description | +| ------------------ | --------- | ------- | ------------------------------------------------ | +| `pluginId` | `string` | — | Required. Identifies this plugin in the event system. | +| `debug` | `boolean` | `false` | Enable verbose console logging. | +| `enabled` | `boolean` | `true` | Whether the client connects to the event bus. | +| `reconnectEveryMs` | `number` | `300` | Interval (ms) between connection retry attempts. | + +See the [Event System](./event-system) page for the full connection lifecycle details. + +## Step 3: Emit Events From Your Code + +Call `emit()` from your library code whenever something interesting happens. You pass only the **suffix** part of the event name — the `pluginId` is prepended automatically. + +```ts +function dispatch(action, payload) { + // Your library logic + state = reducer(state, action, payload) + + // Emit to devtools + storeInspector.emit('state-changed', { + storeName: 'main', + state, + timestamp: Date.now(), + }) + storeInspector.emit('action-dispatched', { + storeName: 'main', + action, + payload, + }) +} +``` + +Common patterns for where to call `emit()`: + +- **In state mutations** — emit the new state after every update (as shown above). +- **In observers or subscriptions** — if your library uses a subscriber/observer pattern, emit from the notification callback. +- **In middleware** — if your library supports middleware or interceptors, emit from a middleware layer so it works automatically for all operations. + +If the devtools are not yet mounted when you emit, events are queued and flushed once the connection succeeds. If the connection never succeeds (e.g., devtools are not present), events are silently dropped after 5 retries. This means you can leave `emit()` calls in your library code without worrying about whether the devtools are active. + +## Step 4: Build the Panel Component + +Create a component that listens for events via `on()` and renders the data. Here is a React example: + +```tsx +import { useState, useEffect } from 'react' +import { storeInspector } from './store-inspector-client' + +function StoreInspectorPanel() { + const [state, setState] = useState>({}) + const [actions, setActions] = useState>([]) + + useEffect(() => { + const cleanupState = storeInspector.on('state-changed', (e) => { + setState(prev => ({ ...prev, [e.payload.storeName]: e.payload.state })) + }) + const cleanupActions = storeInspector.on('action-dispatched', (e) => { + setActions(prev => [...prev, { action: e.payload.action, payload: e.payload.payload }]) + }) + return () => { cleanupState(); cleanupActions() } + }, []) + + return ( +
+

Current State

+
{JSON.stringify(state, null, 2)}
+

Action Log

+
    + {actions.map((a, i) =>
  • {a.action}: {JSON.stringify(a.payload)}
  • )} +
+
+ ) +} +``` + +Like `emit()`, the `on()` method takes only the suffix. The callback receives the full event object with a typed `payload` property. Each `on()` call returns a cleanup function that removes the listener. + +> [!NOTE] +> When using plugin factories from `@tanstack/devtools-utils` (covered below), your panel component receives a `theme` prop (`'light' | 'dark'`) so you can adapt your UI to the current devtools theme. + +## Step 5: Register the Plugin + +Pass your plugin to the devtools component's `plugins` array: + +```tsx +import { TanStackDevtools } from '@tanstack/react-devtools' +import { StoreInspectorPanel } from './StoreInspectorPanel' + +function App() { + return ( + <> + {/* Your app */} + , + }, + ]} + /> + + ) +} +``` + +The `name` is displayed as the tab title in the devtools sidebar. The `render` field accepts a JSX element (React, Preact) or a component reference (Vue, Solid), depending on your adapter. + +You can also pass optional fields: + +- **`id`** — A stable identifier for the plugin. If omitted, one is generated from the name. +- **`defaultOpen`** — Set to `true` to open the plugin's panel automatically on first load. + +See the [Plugin Lifecycle](./plugin-lifecycle) page for the full plugin interface and mount sequence. + +## Advanced: Bidirectional Communication + +Plugins are not limited to one-way data display. You can also send commands from the devtools panel back to your application — for example, "reset state", "replay action", or "toggle feature flag". The same `EventClient` instance handles both directions: your app emits events that the panel listens to, and the panel emits events that your app listens to. + +For a detailed walkthrough with examples, see the [Bidirectional Communication](./bidirectional-communication) guide. + +## Advanced: Plugin Factories + +The `@tanstack/devtools-utils` package provides factory functions that simplify plugin creation for each framework: + +- `createReactPlugin()` — for React plugins +- `createSolidPlugin()` — for Solid plugins +- `createVuePlugin()` — for Vue plugins +- `createPreactPlugin()` — for Preact plugins + +These factories handle the wiring between your component and the devtools container, pass the `theme` prop automatically, and return a `[Plugin, NoOpPlugin]` tuple so you can tree-shake the devtools out of production builds. + +For usage details, see the [Using devtools-utils](./devtools-utils) guide. + +## Publishing to the Marketplace + +Once your plugin is working, you can share it with the community by publishing it to npm and submitting it to the TanStack Devtools Marketplace. The marketplace is a registry of third-party plugins that users can discover and install directly from the devtools UI. + +For submission instructions and the registry format, see [Third-party Plugins](./third-party-plugins). From 5d277372a708c070a3c4cf8f01828e95ad95d047 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:13:20 +0100 Subject: [PATCH 12/21] docs: add devtools-utils guide for plugin factory helpers --- docs/devtools-utils.md | 273 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 docs/devtools-utils.md diff --git a/docs/devtools-utils.md b/docs/devtools-utils.md new file mode 100644 index 00000000..5f581c20 --- /dev/null +++ b/docs/devtools-utils.md @@ -0,0 +1,273 @@ +--- +title: Using devtools-utils +id: devtools-utils +--- + +`@tanstack/devtools-utils` provides factory functions that simplify creating devtools plugins for each framework. Instead of manually wiring up render functions and no-op variants, these helpers produce correctly-typed plugin objects (and their production-safe no-op counterparts) from your components. Each framework has its own subpath export with an API tailored to that framework's conventions. + +## Installation + +```bash +npm install @tanstack/devtools-utils +``` + +## DevtoolsPanelProps + +Every panel component receives a `theme` prop so the panel can match the devtools shell appearance. The interface is defined per-framework in each subpath: + +```ts +interface DevtoolsPanelProps { + theme?: 'light' | 'dark' +} +``` + +> The Vue variant additionally accepts `'system'` as a theme value. + +Import it from the framework-specific subpath: + +```ts +// React +import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/react' + +// Solid +import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/solid' + +// Preact +import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/preact' + +// Vue +import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/vue' +``` + +## React + +### createReactPlugin + +Creates a `[Plugin, NoOpPlugin]` tuple from a React component and plugin metadata. + +**Signature:** + +```ts +function createReactPlugin(options: { + name: string + id?: string + defaultOpen?: boolean + Component: (props: DevtoolsPanelProps) => JSX.Element +}): readonly [Plugin, NoOpPlugin] +``` + +**Usage:** + +```tsx +import { createReactPlugin } from '@tanstack/devtools-utils/react' + +const [MyPlugin, NoOpPlugin] = createReactPlugin({ + name: 'My Store', + id: 'my-store', + defaultOpen: false, + Component: ({ theme }) => , +}) +``` + +The returned tuple contains two factory functions: + +- **`Plugin()`** -- returns a plugin object with `name`, `id`, `defaultOpen`, and a `render` function that renders your `Component` with the current theme. +- **`NoOpPlugin()`** -- returns a plugin object with the same metadata but a `render` function that renders an empty fragment. Use this for production builds where you want to strip devtools out. + +A common pattern for tree-shaking: + +```tsx +const [MyPlugin, NoOpPlugin] = createReactPlugin({ /* ... */ }) + +const ActivePlugin = process.env.NODE_ENV === 'development' ? MyPlugin : NoOpPlugin +``` + +### createReactPanel + +For library authors shipping a class-based devtools core that exposes `mount(el, theme)` and `unmount()` methods. This factory wraps that class in a React component that handles mounting into a `div`, passing the theme, and cleaning up on unmount. + +**Signature:** + +```ts +function createReactPanel< + TComponentProps extends DevtoolsPanelProps | undefined, + TCoreDevtoolsClass extends { + mount: (el: HTMLElement, theme: 'light' | 'dark') => void + unmount: () => void + }, +>(CoreClass: new () => TCoreDevtoolsClass): readonly [Panel, NoOpPanel] +``` + +**Usage:** + +```tsx +import { createReactPanel } from '@tanstack/devtools-utils/react' + +class MyDevtoolsCore { + mount(el: HTMLElement, theme: 'light' | 'dark') { + // render your devtools UI into el + } + unmount() { + // cleanup + } +} + +const [MyPanel, NoOpPanel] = createReactPanel(MyDevtoolsCore) + +// Then use the panel component inside createReactPlugin: +const [MyPlugin, NoOpPlugin] = createReactPlugin({ + name: 'My Store', + Component: MyPanel, +}) +``` + +The returned `Panel` component: +- Creates a `div` with `height: 100%` and stores a ref to it. +- Instantiates `CoreClass` on mount and calls `core.mount(el, theme)`. +- Calls `core.unmount()` on cleanup. +- Re-mounts when the `theme` prop changes. + +`NoOpPanel` renders an empty fragment and does nothing. + +## Preact + +### createPreactPlugin + +Identical API to `createReactPlugin`, using Preact's JSX types. Import from `@tanstack/devtools-utils/preact`. + +**Signature:** + +```ts +function createPreactPlugin(options: { + name: string + id?: string + defaultOpen?: boolean + Component: (props: DevtoolsPanelProps) => JSX.Element +}): readonly [Plugin, NoOpPlugin] +``` + +**Usage:** + +```tsx +import { createPreactPlugin } from '@tanstack/devtools-utils/preact' + +const [MyPlugin, NoOpPlugin] = createPreactPlugin({ + name: 'My Store', + id: 'my-store', + Component: ({ theme }) => , +}) +``` + +The return value and behavior are the same as `createReactPlugin` -- a `[Plugin, NoOpPlugin]` tuple where `Plugin` renders your component and `NoOpPlugin` renders nothing. + +### createPreactPanel + +Also available for Preact with the same class-based API as `createReactPanel`: + +```ts +import { createPreactPanel } from '@tanstack/devtools-utils/preact' + +const [MyPanel, NoOpPanel] = createPreactPanel(MyDevtoolsCore) +``` + +## Solid + +### createSolidPlugin + +Same option-object API as React and Preact, using Solid's JSX types. Import from `@tanstack/devtools-utils/solid`. + +**Signature:** + +```ts +function createSolidPlugin(options: { + name: string + id?: string + defaultOpen?: boolean + Component: (props: DevtoolsPanelProps) => JSX.Element +}): readonly [Plugin, NoOpPlugin] +``` + +**Usage:** + +```tsx +import { createSolidPlugin } from '@tanstack/devtools-utils/solid' + +const [MyPlugin, NoOpPlugin] = createSolidPlugin({ + name: 'My Store', + id: 'my-store', + Component: (props) => , +}) +``` + +### createSolidPanel + +Solid also provides a class-based panel factory. It uses `createSignal` and `onMount`/`onCleanup` instead of React hooks: + +```ts +import { createSolidPanel } from '@tanstack/devtools-utils/solid' + +const [MyPanel, NoOpPanel] = createSolidPanel(MyDevtoolsCore) +``` + +## Vue + +### createVuePlugin + +The Vue factory has a different API from the JSX-based frameworks. It takes a `name` string and a Vue `DefineComponent` as separate arguments rather than an options object. + +**Signature:** + +```ts +function createVuePlugin>( + name: string, + component: DefineComponent, +): readonly [Plugin, NoOpPlugin] +``` + +**Usage:** + +```ts +import { createVuePlugin } from '@tanstack/devtools-utils/vue' +import MyStorePanel from './MyStorePanel.vue' + +const [MyPlugin, NoOpPlugin] = createVuePlugin('My Store', MyStorePanel) +``` + +The returned functions differ from the JSX-based variants: + +- **`Plugin(props)`** -- returns `{ name, component, props }` where `component` is your Vue component. +- **`NoOpPlugin(props)`** -- returns `{ name, component: Fragment, props }` where the component is Vue's built-in `Fragment` (renders nothing visible). + +Both accept props that get forwarded to the component. + +### createVuePanel + +For class-based devtools cores, Vue provides `createVuePanel`. It creates a Vue `defineComponent` that handles mounting and unmounting the core class: + +```ts +import { createVuePanel } from '@tanstack/devtools-utils/vue' + +const [MyPanel, NoOpPanel] = createVuePanel(MyDevtoolsCore) +``` + +The panel component accepts `theme` and `devtoolsProps` as props. It mounts the core instance into a `div` element on `onMounted` and calls `unmount()` on `onUnmounted`. + +## When to Use Factories vs Manual Plugin Objects + +**Use the factories** when you are building a reusable library plugin that will be published as a package. The factories ensure: + +- Consistent plugin object shape across frameworks. +- A matching `NoOpPlugin` variant for production tree-shaking. +- Correct typing without manual type annotations. + +**Use manual plugin objects** when you are building a one-off internal devtools panel for your application. In that case, passing `name` and `render` directly to the devtools configuration is simpler and avoids the extra abstraction: + +```tsx +// Manual approach -- fine for one-off panels +{ + name: 'App State', + render: (el, theme) => , +} +``` + +The factory approach becomes more valuable as you add `id`, `defaultOpen`, and need both a development and production variant of the same plugin. From cea62b0a6d026e0470654d18689dddf66f3611d8 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:13:34 +0100 Subject: [PATCH 13/21] docs: add bidirectional communication guide --- docs/bidirectional-communication.md | 128 ++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 docs/bidirectional-communication.md diff --git a/docs/bidirectional-communication.md b/docs/bidirectional-communication.md new file mode 100644 index 00000000..843dc7bc --- /dev/null +++ b/docs/bidirectional-communication.md @@ -0,0 +1,128 @@ +--- +title: Bidirectional Communication +id: bidirectional-communication +--- + +Most devtools plugins observe state in one direction: app to devtools. But `EventClient` supports two-way communication. Your devtools panel can also send commands back to the app. This enables features like state editing, action replay, and time-travel debugging. + +## Pattern: App to Devtools (Observation) + +The standard one-way pattern. Your app emits events, the devtools panel listens. + +```ts +// In your app/library +eventClient.emit('state-update', { count: 42 }) + +// In your devtools panel +eventClient.on('state-update', (e) => { + setState(e.payload) +}) +``` + +## Pattern: Devtools to App (Commands) + +The panel emits command events, the app listens and reacts. + +```ts +// In your devtools panel (e.g., a "Reset" button click handler) +eventClient.emit('reset', undefined) + +// In your app/library +eventClient.on('reset', () => { + store.reset() +}) +``` + +You need to define both directions in your event map: + +```ts +type MyEvents = { + // App → Devtools + 'my-plugin:state-update': { count: number } + // Devtools → App + 'my-plugin:reset': void + 'my-plugin:set-state': { count: number } +} +``` + +## Pattern: Time Travel + +The most powerful bidirectional pattern. Combine observation with command-based state restoration. + +### Event Map + +```ts +type TimeTravelEvents = { + 'time-travel:snapshot': { state: unknown; timestamp: number; label: string } + 'time-travel:revert': { state: unknown } +} +``` + +### App Side + +Emit snapshots on every state change: + +```ts +function applyAction(action) { + state = reducer(state, action) + + timeTravelClient.emit('snapshot', { + state: structuredClone(state), + timestamp: Date.now(), + label: action.type, + }) +} + +// Listen for revert commands from devtools +timeTravelClient.on('revert', (e) => { + state = e.payload.state + rerender() +}) +``` + +### Panel Side + +Collect snapshots and provide a slider: + +```tsx +function TimeTravelPanel() { + const [snapshots, setSnapshots] = useState([]) + const [index, setIndex] = useState(0) + + useEffect(() => { + return timeTravelClient.on('snapshot', (e) => { + setSnapshots((prev) => [...prev, e.payload]) + setIndex((prev) => prev + 1) + }) + }, []) + + const handleSliderChange = (newIndex) => { + setIndex(newIndex) + timeTravelClient.emit('revert', { state: snapshots[newIndex].state }) + } + + return ( +
+ handleSliderChange(Number(e.target.value))} + /> +

+ State at: {snapshots[index]?.label} ( + {new Date(snapshots[index]?.timestamp).toLocaleTimeString()}) +

+
{JSON.stringify(snapshots[index]?.state, null, 2)}
+
+ ) +} +``` + +## Best Practices + +- **Keep payloads serializable.** No functions, DOM nodes, or circular references. +- **Use `structuredClone()` for snapshots** to avoid reference mutations. +- **Debounce frequent emissions** if needed (e.g., rapid state changes). +- **Use distinct event suffixes** for observation vs commands (e.g., `state-update` for observation, `set-state` for commands). From fc23c8795c8e09ea4222989186df83670d9761da Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:13:43 +0100 Subject: [PATCH 14/21] docs: add Vue basic setup and adapter pages --- docs/framework/vue/adapter.md | 76 +++++++++++++++++++++++++++++++ docs/framework/vue/basic-setup.md | 62 +++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 docs/framework/vue/adapter.md create mode 100644 docs/framework/vue/basic-setup.md diff --git a/docs/framework/vue/adapter.md b/docs/framework/vue/adapter.md new file mode 100644 index 00000000..bbf6156a --- /dev/null +++ b/docs/framework/vue/adapter.md @@ -0,0 +1,76 @@ +--- +title: TanStack Devtools Vue Adapter +id: adapter +--- + +The Vue adapter wraps `TanStackDevtoolsCore` in a Vue 3 component, using Vue's `` to render plugins and their tab titles into the correct DOM containers managed by the devtools shell. + +## Installation + +```sh +npm install @tanstack/vue-devtools +``` + +## Component Props + +The `TanStackDevtools` component accepts the following props, defined by the `TanStackDevtoolsVueInit` interface: + +| Prop | Type | Description | +| --- | --- | --- | +| `plugins` | `TanStackDevtoolsVuePlugin[]` | Array of plugins to render inside the devtools panel. | +| `config` | `Partial` | Configuration for the devtools shell. Sets the initial state on first load; afterwards settings are persisted in local storage. | +| `eventBusConfig` | `ClientEventBusConfig` | Configuration for the TanStack Devtools client event bus. | + +## Plugin Type + +Each plugin in the `plugins` array must conform to the `TanStackDevtoolsVuePlugin` type: + +```ts +type TanStackDevtoolsVuePlugin = { + id?: string + component: Component + name: string | Component + props?: Record +} +``` + +| Field | Type | Description | +| --- | --- | --- | +| `id` | `string` (optional) | Unique identifier for the plugin. | +| `component` | `Component` | The Vue component to render as the plugin panel content. | +| `name` | `string \| Component` | Display name for the tab title. Can be a plain string or a Vue component for custom rendering. | +| `props` | `Record` (optional) | Additional props passed to the plugin component via `v-bind`. | + +## Key Difference from React + +The Vue adapter uses `component` (a Vue component reference) instead of `render` (a JSX element) in plugin definitions. Props are provided through the `props` field and bound to the component with `v-bind`, rather than being embedded directly in a JSX expression. + +```vue + + + + +``` + +## Exports + +The `@tanstack/vue-devtools` package exports: + +- **`TanStackDevtools`** -- The main Vue component that renders the devtools panel. +- **`TanStackDevtoolsVuePlugin`** (type) -- The type for plugin definitions. +- **`TanStackDevtoolsVueInit`** (type) -- The props interface for the `TanStackDevtools` component. + +The package depends on `@tanstack/devtools` (the core package), which provides `TanStackDevtoolsCore`, `TanStackDevtoolsConfig`, `ClientEventBusConfig`, and other core utilities. diff --git a/docs/framework/vue/basic-setup.md b/docs/framework/vue/basic-setup.md new file mode 100644 index 00000000..f0b7f48b --- /dev/null +++ b/docs/framework/vue/basic-setup.md @@ -0,0 +1,62 @@ +--- +title: Basic setup +id: basic-setup +--- + +TanStack Devtools provides you with an easy-to-use and modular client that allows you to compose multiple devtools into one easy-to-use panel. + +## Setup + +Install the [TanStack Devtools](https://www.npmjs.com/package/@tanstack/vue-devtools) library. This will install the devtools core as well as provide you with the Vue-specific adapter. + +```bash +npm i @tanstack/vue-devtools +``` + +Next, in the root of your application, import the `TanStackDevtools` component from `@tanstack/vue-devtools` and add it to your template. + +```vue + + + +``` + +Import the desired devtools and provide them to the `TanStackDevtools` component via the `plugins` prop along with a label for the menu. + +Currently TanStack offers: + +- `VueQueryDevtoolsPanel` + +```vue + + + +``` + +> Note: The Vue adapter uses `component` instead of `render` in plugin definitions. In Vue, components are passed as component references rather than JSX elements, and any additional props can be provided via the `props` field. + +Finally, add any additional configuration you desire to the `TanStackDevtools` component. More information can be found under the [TanStack Devtools Configuration](../../configuration.md) section. + +A complete working example can be found in our [basic example](https://tanstack.com/devtools/latest/docs/framework/vue/examples/basic). From e59d87d06492720ae0567d4f66e3b5e80cd20465 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:13:51 +0100 Subject: [PATCH 15/21] docs: add Vue and Solid custom plugins guides --- docs/framework/solid/guides/custom-plugins.md | 197 +++++++++++++++++ docs/framework/vue/guides/custom-plugins.md | 204 ++++++++++++++++++ 2 files changed, 401 insertions(+) create mode 100644 docs/framework/solid/guides/custom-plugins.md create mode 100644 docs/framework/vue/guides/custom-plugins.md diff --git a/docs/framework/solid/guides/custom-plugins.md b/docs/framework/solid/guides/custom-plugins.md new file mode 100644 index 00000000..d7d597d9 --- /dev/null +++ b/docs/framework/solid/guides/custom-plugins.md @@ -0,0 +1,197 @@ +--- +title: Custom plugins +id: custom-plugins +--- + +TanStack devtools allows you to create your own custom plugins by emitting and listening to our event bus. + +## Prerequisite + +This guide will walk you through a simple example where our library is a counter with a count history. A working example can be found in our [custom-plugin example](https://tanstack.com/devtools/latest/docs/framework/react/examples/custom-devtools). + +This is our library code: + +counter.ts +```tsx +export function createCounter() { + let count = 0 + const history = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + }, + decrement: () => { + count-- + history.push(count) + }, + }; +} +``` + +## Event Client Setup + +Install the [TanStack Devtools Event Client](https://www.npmjs.com/package/@tanstack/devtools-event-client) utils. + +```bash +npm i @tanstack/devtools-event-client +``` + +First you will need to setup the `EventClient`. + +eventClient.ts +```tsx +import { EventClient } from '@tanstack/devtools-event-client' + + +type EventMap = { + // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The value is the expected type of the event payload + 'custom-devtools:counter-state': { count: number, history: number[] } +} + +class CustomEventClient extends EventClient { + constructor() { + super({ + // The pluginId must match that of the event map key + pluginId: 'custom-devtools', + }) + } +} + +// This is where the magic happens, it'll be used throughout your application. +export const DevtoolsEventClient = new CustomEventClient() +``` + +## Event Client Integration + +Now we need to hook our `EventClient` into the application code. This can be done in many way's, a useEffect that emits the current state, or a subscription to an observer, all that matters is that when you want to emit the current state you do the following. + +Our new library code will looks as follows: + +counter.ts +```tsx +import { DevtoolsEventClient } from './eventClient.ts' + +export function createCounter() { + let count = 0 + const history: Array = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + + // The emit eventSuffix must match that of the EventMap defined in eventClient.ts + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + decrement: () => { + count-- + history.push(count) + + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + } +} +``` + +> [!IMPORTANT] +> `EventClient` is framework agnostic so this process will be the same regardless of framework or even in vanilla JavaScript. + +## Consuming The Event Client + +Now we need to create our devtools panel, for a simple approach write the devtools in the framework that the adapter is, be aware that this will make the plugin framework specific. + +> Because TanStack is framework agnostic we have taken a more complicated approach that will be explained in coming docs (if framework agnosticism is not a concern to you, you can ignore this). + +DevtoolPanel.tsx +```tsx +import { createSignal, onCleanup } from 'solid-js' +import { DevtoolsEventClient } from './eventClient' + +export function DevtoolPanel() { + const [state, setState] = createSignal<{ count: number; history: number[] }>() + + const cleanup = DevtoolsEventClient.on('counter-state', (e) => setState(e.payload)) + onCleanup(cleanup) + + return ( +
+
{state()?.count}
+
{JSON.stringify(state()?.history)}
+
+ ) +} +``` + +## Application Integration + +This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. + +index.tsx +```tsx +import { render } from 'solid-js/web' +import { TanStackDevtools } from '@tanstack/solid-devtools' +import { DevtoolPanel } from './DevtoolPanel' + +render(() => ( + <> + + , + }, + ]} + /> + +), document.getElementById('root')!) +``` + +## Debugging + +Both the `TanStackDevtools` component and the TanStack `EventClient` come with built in debug mode which will log to the console the emitted event as well as the EventClient status. + +TanStackDevtool's debugging mode can be activated like so: +```tsx + , + }, + ]} +/> +``` + +Where as the EventClient's debug mode can be activated by: +```tsx +class CustomEventClient extends EventClient { + constructor() { + super({ + pluginId: 'custom-devtools', + debug: true, + }) + } +} +``` + +Activating the debug mode will log to the console the current events that emitter has emitted or listened to. The EventClient will have appended `[tanstack-devtools:${pluginId}]` and the client will have appended `[tanstack-devtools:client-bus]`. + +Heres an example of both: +``` +[tanstack-devtools:client-bus] Initializing client event bus + +[tanstack-devtools:custom-devtools-plugin] Registered event to bus custom-devtools:counter-state +``` diff --git a/docs/framework/vue/guides/custom-plugins.md b/docs/framework/vue/guides/custom-plugins.md new file mode 100644 index 00000000..e26b6162 --- /dev/null +++ b/docs/framework/vue/guides/custom-plugins.md @@ -0,0 +1,204 @@ +--- +title: Custom plugins +id: custom-plugins +--- + +TanStack devtools allows you to create your own custom plugins by emitting and listening to our event bus. + +## Prerequisite + +This guide will walk you through a simple example where our library is a counter with a count history. A working example can be found in our [custom-plugin example](https://tanstack.com/devtools/latest/docs/framework/react/examples/custom-devtools). + +This is our library code: + +counter.ts +```tsx +export function createCounter() { + let count = 0 + const history = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + }, + decrement: () => { + count-- + history.push(count) + }, + }; +} +``` + +## Event Client Setup + +Install the [TanStack Devtools Event Client](https://www.npmjs.com/package/@tanstack/devtools-event-client) utils. + +```bash +npm i @tanstack/devtools-event-client +``` + +First you will need to setup the `EventClient`. + +eventClient.ts +```tsx +import { EventClient } from '@tanstack/devtools-event-client' + + +type EventMap = { + // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The value is the expected type of the event payload + 'custom-devtools:counter-state': { count: number, history: number[] } +} + +class CustomEventClient extends EventClient { + constructor() { + super({ + // The pluginId must match that of the event map key + pluginId: 'custom-devtools', + }) + } +} + +// This is where the magic happens, it'll be used throughout your application. +export const DevtoolsEventClient = new CustomEventClient() +``` + +## Event Client Integration + +Now we need to hook our `EventClient` into the application code. This can be done in many way's, a useEffect that emits the current state, or a subscription to an observer, all that matters is that when you want to emit the current state you do the following. + +Our new library code will looks as follows: + +counter.ts +```tsx +import { DevtoolsEventClient } from './eventClient.ts' + +export function createCounter() { + let count = 0 + const history: Array = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + + // The emit eventSuffix must match that of the EventMap defined in eventClient.ts + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + decrement: () => { + count-- + history.push(count) + + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + } +} +``` + +> [!IMPORTANT] +> `EventClient` is framework agnostic so this process will be the same regardless of framework or even in vanilla JavaScript. + +## Consuming The Event Client + +Now we need to create our devtools panel, for a simple approach write the devtools in the framework that the adapter is, be aware that this will make the plugin framework specific. + +> Because TanStack is framework agnostic we have taken a more complicated approach that will be explained in coming docs (if framework agnosticism is not a concern to you, you can ignore this). + +DevtoolPanel.vue +```vue + + + +``` + +## Application Integration + +This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. + +App.vue +```vue + + + +``` + +## Debugging + +Both the `TanStackDevtools` component and the TanStack `EventClient` come with built in debug mode which will log to the console the emitted event as well as the EventClient status. + +TanStackDevtool's debugging mode can be activated like so: +```vue + +``` + +Where as the EventClient's debug mode can be activated by: +```tsx +class CustomEventClient extends EventClient { + constructor() { + super({ + pluginId: 'custom-devtools', + debug: true, + }) + } +} +``` + +Activating the debug mode will log to the console the current events that emitter has emitted or listened to. The EventClient will have appended `[tanstack-devtools:${pluginId}]` and the client will have appended `[tanstack-devtools:client-bus]`. + +Heres an example of both: +``` +[tanstack-devtools:client-bus] Initializing client event bus + +[tanstack-devtools:custom-devtools-plugin] Registered event to bus custom-devtools:counter-state +``` From 78818b8e1f0e242829948f540993186f1082a275 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:19:35 +0100 Subject: [PATCH 16/21] docs: replace TODO in React adapter with actual content --- docs/framework/react/adapter.md | 70 ++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/docs/framework/react/adapter.md b/docs/framework/react/adapter.md index 408446b2..bb5426bd 100644 --- a/docs/framework/react/adapter.md +++ b/docs/framework/react/adapter.md @@ -11,6 +11,72 @@ If you are using TanStack Devtools in a React application, we recommend using th npm install @tanstack/react-devtools ``` -## React Hooks +## Component: TanStackDevtools -TODO +The main React component for rendering devtools. It accepts a single props object of type `TanStackDevtoolsReactInit`: + +| Prop | Type | Description | +| --- | --- | --- | +| `plugins` | `TanStackDevtoolsReactPlugin[]` | Array of plugins to display in the devtools panel | +| `config` | `TanStackDevtoolsReactConfig` | Devtools UI configuration (position, hotkeys, theme, custom trigger, etc.) | +| `eventBusConfig` | `ClientEventBusConfig` | Event bus connection settings for communicating with the server bus | + +## Plugin Type: TanStackDevtoolsReactPlugin + +Each plugin describes a tab in the devtools panel: + +```ts +type PluginRender = + | JSX.Element + | ((el: HTMLElement, theme: 'dark' | 'light') => JSX.Element) + +type TanStackDevtoolsReactPlugin = { + id?: string + name: string | PluginRender + render: PluginRender + defaultOpen?: boolean +} +``` + +- **`render`** can be a JSX element (simplest -- just pass ``) or a function that receives the container element and current theme. The function form is useful when you need to access the raw DOM element or respond to theme changes. +- **`name`** works the same way -- use a string for plain text, or JSX / a function for custom tab titles. +- **`id`** is an optional unique identifier. If omitted, it is generated from the name. +- **`defaultOpen`** marks the plugin as initially active when no other plugins are open. + +## Usage Example + +```tsx +import { TanStackDevtools } from '@tanstack/react-devtools' +import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools' + +function App() { + return ( + <> + + , + defaultOpen: true, + }, + ]} + /> + + ) +} +``` + +## RSC Compatibility + +The adapter includes a `'use client'` directive at the top of its entry file, making it compatible with React Server Components. You can import it directly in your client components without needing to add your own `'use client'` boundary. + +## Exports + +The `@tanstack/react-devtools` package exports: + +- **`TanStackDevtools`** -- the main component +- **`TanStackDevtoolsReactPlugin`** -- type for plugin objects +- **`TanStackDevtoolsReactInit`** -- type for the component's props From becb4a4a04812a2e14d2b29e6f25a8dca6914e04 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:19:40 +0100 Subject: [PATCH 17/21] docs: expand Vite plugin features documentation --- docs/vite-plugin.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/vite-plugin.md b/docs/vite-plugin.md index cde63117..ae584417 100644 --- a/docs/vite-plugin.md +++ b/docs/vite-plugin.md @@ -204,13 +204,33 @@ export default { ## Features -### Go to source +### Go to Source -Allows you to open the source location on anything in your browser by clicking on it. +The "Go to Source" feature lets you click on any element in your browser and open its source file in your editor at the exact line where it's defined. It works by injecting `data-tsd-source` attributes into your components via a Babel transformation during development. These attributes encode the file path and line number of each element. -To trigger this behavior you need the Devtools Vite plugin installed and configured and -the Panel available on the page. Simply click on any element while holding down the Shift and Ctrl (or Meta) keys. +To use it, activate the source inspector by holding the inspect hotkey (default: Shift+Alt+Ctrl/Meta). An overlay will highlight elements under your cursor and display their source location. Clicking on a highlighted element opens the corresponding file in your editor at the exact line, powered by `launch-editor` under the hood. -### Advanced console logs +For a complete guide on configuration and usage, see the [Source Inspector](./source-inspector) docs. -Allows you to go directly to the console log location directly from the browser/terminal +### Console Piping + +When enabled (default), `console.log()` and other console methods in the browser are piped to your terminal, and server-side console output appears in the browser console. This is particularly useful when debugging SSR or API routes — you see all logs in one place without switching between terminal and browser. Configure which log levels are piped via `consolePiping.levels`. + +### Enhanced Logs + +Console logs are enhanced with clickable source locations. In the browser console, each log shows the file and line number where it originated. Click to open the source file in your editor. Enable/disable via the `enhancedLogs` config option. + +### Production Build Stripping + +By default (`removeDevtoolsOnBuild: true`), the Vite plugin replaces all devtools imports with empty modules in production builds. This includes: +- `@tanstack/react-devtools` +- `@tanstack/vue-devtools` +- `@tanstack/solid-devtools` +- `@tanstack/preact-devtools` +- `@tanstack/devtools` + +This ensures zero devtools code reaches production. Set `removeDevtoolsOnBuild: false` to keep devtools in production (see [Production](./production) docs). + +### Plugin Marketplace + +The Vite plugin enables the in-devtools plugin marketplace. When you browse available plugins in the devtools Settings tab and click "Install", the Vite plugin handles the npm/pnpm/yarn installation and automatically injects the plugin import into your devtools setup file. This only works during development with the Vite dev server running. From 4cdd52939a2deaff9c36a93ec45c0becb7554088 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:37:02 +0100 Subject: [PATCH 18/21] docs: fix event map key convention, typos, and internal link extensions - Event map keys should be suffix-only (e.g., 'state-update'), not prefixed with pluginId. EventClient prepends pluginId automatically. Fixed across all custom plugin guides and concept pages. - Fix enabled:false docs in event-system.md (emit is a no-op, not internal dispatch) - Fix double "the the" typo in installation.md - Remove .md extensions from internal doc links for consistency - Fix event map key in configuration.md example --- docs/bidirectional-communication.md | 10 +++++----- docs/building-custom-plugins.md | 8 ++++---- docs/configuration.md | 2 +- docs/event-system.md | 14 +++++++------- docs/framework/preact/basic-setup.md | 2 +- docs/framework/preact/guides/custom-plugins.md | 8 ++++---- docs/framework/react/basic-setup.md | 2 +- docs/framework/react/guides/custom-plugins.md | 8 ++++---- docs/framework/solid/basic-setup.md | 2 +- docs/framework/solid/guides/custom-plugins.md | 8 ++++---- docs/framework/vue/basic-setup.md | 2 +- docs/framework/vue/guides/custom-plugins.md | 8 ++++---- docs/installation.md | 4 ++-- docs/quick-start.md | 2 +- 14 files changed, 40 insertions(+), 40 deletions(-) diff --git a/docs/bidirectional-communication.md b/docs/bidirectional-communication.md index 843dc7bc..2a8c55b4 100644 --- a/docs/bidirectional-communication.md +++ b/docs/bidirectional-communication.md @@ -38,10 +38,10 @@ You need to define both directions in your event map: ```ts type MyEvents = { // App → Devtools - 'my-plugin:state-update': { count: number } + 'state-update': { count: number } // Devtools → App - 'my-plugin:reset': void - 'my-plugin:set-state': { count: number } + 'reset': void + 'set-state': { count: number } } ``` @@ -53,8 +53,8 @@ The most powerful bidirectional pattern. Combine observation with command-based ```ts type TimeTravelEvents = { - 'time-travel:snapshot': { state: unknown; timestamp: number; label: string } - 'time-travel:revert': { state: unknown } + 'snapshot': { state: unknown; timestamp: number; label: string } + 'revert': { state: unknown } } ``` diff --git a/docs/building-custom-plugins.md b/docs/building-custom-plugins.md index 5ddbbf59..94f0ab2d 100644 --- a/docs/building-custom-plugins.md +++ b/docs/building-custom-plugins.md @@ -18,13 +18,13 @@ Start by creating a TypeScript type that maps event names to their payload types ```ts type StoreEvents = { - 'store-inspector:state-changed': { storeName: string; state: unknown; timestamp: number } - 'store-inspector:action-dispatched': { storeName: string; action: string; payload: unknown } - 'store-inspector:reset': void + 'state-changed': { storeName: string; state: unknown; timestamp: number } + 'action-dispatched': { storeName: string; action: string; payload: unknown } + 'reset': void } ``` -Every key in the event map follows the `{pluginId}:{suffix}` naming convention. The `pluginId` portion (`store-inspector` in this example) **must** match the `pluginId` you pass to the `EventClient` constructor in the next step. The suffix (`state-changed`, `action-dispatched`, `reset`) is whatever name makes sense for your event. +Each key in the event map is just the event name (the suffix). Do **not** include the `pluginId` in the key — the `EventClient` prepends the `pluginId` automatically when emitting and listening. For example, if `pluginId` is `'store-inspector'` and the key is `'state-changed'`, the fully qualified event dispatched on the bus will be `'store-inspector:state-changed'`. The value of each key is the payload type. Use `void` for events that carry no data. diff --git a/docs/configuration.md b/docs/configuration.md index a49d5db3..1e742efe 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -150,7 +150,7 @@ Put together the `EventClient` configuration looks like: import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - 'custom-devtools:custom-state': { state: string } + 'custom-state': { state: string } } class customEventClient extends EventClient { diff --git a/docs/event-system.md b/docs/event-system.md index d440e710..1fd423d8 100644 --- a/docs/event-system.md +++ b/docs/event-system.md @@ -13,8 +13,8 @@ Create a typed `EventClient` by extending the base class with your event map: import { EventClient } from '@tanstack/devtools-event-client' type MyEvents = { - 'my-plugin:state-update': { count: number } - 'my-plugin:action': { type: string } + 'state-update': { count: number } + 'action': { type: string } } class MyEventClient extends EventClient { @@ -37,12 +37,12 @@ The constructor accepts the following options: ## Event Maps and Type Safety -The generic `EventMap` type maps event names to payload types. Keys follow the `{pluginId}:{eventSuffix}` convention: +The generic `EventMap` type maps event names to payload types. Keys are event suffixes only — the `pluginId` is prepended automatically by `EventClient` when emitting and listening: ```ts type MyEvents = { - 'my-plugin:state-update': { count: number } - 'my-plugin:action': { type: string } + 'state-update': { count: number } + 'action': { type: string } } ``` @@ -53,7 +53,7 @@ TypeScript enforces correct event names and payload shapes at compile time. You Call `eventClient.emit(eventSuffix, payload)` to dispatch an event. You pass only the **suffix** (the part after the colon). The `pluginId` is prepended automatically: ```ts -// If pluginId is 'my-plugin' and event map has 'my-plugin:state-update' +// If pluginId is 'my-plugin' and event map has 'state-update' myEventClient.emit('state-update', { count: 42 }) // Dispatches event named 'my-plugin:state-update' ``` @@ -120,7 +120,7 @@ The `EventClient` manages its connection to the event bus automatically: ### The `enabled` Option -When `enabled` is set to `false` in the constructor, the `EventClient` does not connect to the bus at all. Events are only dispatched internally via `EventTarget`, which is useful for components on the same page that do not need server communication. All `on`, `onAll`, and `onAllPluginEvents` calls return no-op cleanup functions when the client is disabled. +When `enabled` is set to `false`, the EventClient is effectively inert — `emit()` is a no-op and `on()` returns a no-op cleanup function. This is useful for conditionally disabling devtools instrumentation (e.g., in production). ## Server Event Bus diff --git a/docs/framework/preact/basic-setup.md b/docs/framework/preact/basic-setup.md index efb501b6..11ec85b8 100644 --- a/docs/framework/preact/basic-setup.md +++ b/docs/framework/preact/basic-setup.md @@ -64,6 +64,6 @@ render( ) ``` -Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration.md) section. +Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration) section. A complete working example can be found in our [basic example](https://tanstack.com/devtools/latest/docs/framework/preact/examples/basic). diff --git a/docs/framework/preact/guides/custom-plugins.md b/docs/framework/preact/guides/custom-plugins.md index c3c3ac1b..ce2d3f0c 100644 --- a/docs/framework/preact/guides/custom-plugins.md +++ b/docs/framework/preact/guides/custom-plugins.md @@ -47,15 +47,15 @@ import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The key is the event suffix only — the pluginId is prepended automatically by EventClient // The value is the expected type of the event payload - 'custom-devtools:counter-state': { count: number, history: number[] } + 'counter-state': { count: number, history: number[] } } class CustomEventClient extends EventClient { constructor() { super({ - // The pluginId must match that of the event map key + // The pluginId is prepended to event map keys when emitting/listening pluginId: 'custom-devtools', }) } @@ -138,7 +138,7 @@ export function DevtoolPanel() { ## Application Integration -This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. As well as the complete [custom-devtools example](https://tanstack.com/devtools/latest/docs/framework/preact/examples/custom-devtools) in our examples section. +This step follows what's shown in [basic-setup](../basic-setup) for a more documented guide go check it out. As well as the complete [custom-devtools example](https://tanstack.com/devtools/latest/docs/framework/preact/examples/custom-devtools) in our examples section. Main.tsx ```tsx diff --git a/docs/framework/react/basic-setup.md b/docs/framework/react/basic-setup.md index aa24ec9d..dfd39c43 100644 --- a/docs/framework/react/basic-setup.md +++ b/docs/framework/react/basic-setup.md @@ -74,6 +74,6 @@ createRoot(document.getElementById('root')!).render( ) ``` -Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration.md) section. +Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration) section. A complete working example can be found in our [basic example](https://tanstack.com/devtools/latest/docs/framework/react/examples/basic). diff --git a/docs/framework/react/guides/custom-plugins.md b/docs/framework/react/guides/custom-plugins.md index 4a700220..66ac8051 100644 --- a/docs/framework/react/guides/custom-plugins.md +++ b/docs/framework/react/guides/custom-plugins.md @@ -47,15 +47,15 @@ import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The key is the event suffix only — the pluginId is prepended automatically by EventClient // The value is the expected type of the event payload - 'custom-devtools:counter-state': { count: number, history: number[] } + 'counter-state': { count: number, history: number[] } } class CustomEventClient extends EventClient { constructor() { super({ - // The pluginId must match that of the event map key + // The pluginId is prepended to event map keys when emitting/listening pluginId: 'custom-devtools', }) } @@ -137,7 +137,7 @@ export function DevtoolPanel() { ## Application Integration -This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. As well as the complete [custom-devtools example](https://tanstack.com/devtools/latest/docs/framework/react/examples/custom-devtools) in our examples section. +This step follows what's shown in [basic-setup](../basic-setup) for a more documented guide go check it out. As well as the complete [custom-devtools example](https://tanstack.com/devtools/latest/docs/framework/react/examples/custom-devtools) in our examples section. Main.tsx ```tsx diff --git a/docs/framework/solid/basic-setup.md b/docs/framework/solid/basic-setup.md index 238dfedf..a7601b30 100644 --- a/docs/framework/solid/basic-setup.md +++ b/docs/framework/solid/basic-setup.md @@ -68,6 +68,6 @@ render(() => ( ), document.getElementById('root')!) ``` -Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration.md) section. +Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration) section. A complete working example can be found in our [examples section](https://tanstack.com/devtools/latest/docs/framework/solid/examples). diff --git a/docs/framework/solid/guides/custom-plugins.md b/docs/framework/solid/guides/custom-plugins.md index d7d597d9..eb5efd8f 100644 --- a/docs/framework/solid/guides/custom-plugins.md +++ b/docs/framework/solid/guides/custom-plugins.md @@ -47,15 +47,15 @@ import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The key is the event suffix only — the pluginId is prepended automatically by EventClient // The value is the expected type of the event payload - 'custom-devtools:counter-state': { count: number, history: number[] } + 'counter-state': { count: number, history: number[] } } class CustomEventClient extends EventClient { constructor() { super({ - // The pluginId must match that of the event map key + // The pluginId is prepended to event map keys when emitting/listening pluginId: 'custom-devtools', }) } @@ -135,7 +135,7 @@ export function DevtoolPanel() { ## Application Integration -This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. +This step follows what's shown in [basic-setup](../basic-setup) for a more documented guide go check it out. index.tsx ```tsx diff --git a/docs/framework/vue/basic-setup.md b/docs/framework/vue/basic-setup.md index f0b7f48b..3e27b964 100644 --- a/docs/framework/vue/basic-setup.md +++ b/docs/framework/vue/basic-setup.md @@ -57,6 +57,6 @@ const plugins: TanStackDevtoolsVuePlugin[] = [ > Note: The Vue adapter uses `component` instead of `render` in plugin definitions. In Vue, components are passed as component references rather than JSX elements, and any additional props can be provided via the `props` field. -Finally, add any additional configuration you desire to the `TanStackDevtools` component. More information can be found under the [TanStack Devtools Configuration](../../configuration.md) section. +Finally, add any additional configuration you desire to the `TanStackDevtools` component. More information can be found under the [TanStack Devtools Configuration](../../configuration) section. A complete working example can be found in our [basic example](https://tanstack.com/devtools/latest/docs/framework/vue/examples/basic). diff --git a/docs/framework/vue/guides/custom-plugins.md b/docs/framework/vue/guides/custom-plugins.md index e26b6162..51942454 100644 --- a/docs/framework/vue/guides/custom-plugins.md +++ b/docs/framework/vue/guides/custom-plugins.md @@ -47,15 +47,15 @@ import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The key is the event suffix only — the pluginId is prepended automatically by EventClient // The value is the expected type of the event payload - 'custom-devtools:counter-state': { count: number, history: number[] } + 'counter-state': { count: number, history: number[] } } class CustomEventClient extends EventClient { constructor() { super({ - // The pluginId must match that of the event map key + // The pluginId is prepended to event map keys when emitting/listening pluginId: 'custom-devtools', }) } @@ -144,7 +144,7 @@ onUnmounted(() => { ## Application Integration -This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. +This step follows what's shown in [basic-setup](../basic-setup) for a more documented guide go check it out. App.vue ```vue diff --git a/docs/installation.md b/docs/installation.md index f65231e0..f1bb2766 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -54,7 +54,7 @@ TanStack Devtools is compatible with Vue 3+ npm install -D @tanstack/devtools ``` -Install the the core `@tanstack/devtools` package to use with any framework or without a framework. Each framework package up above will also re-export everything from this core package. +Install the core `@tanstack/devtools` package to use with any framework or without a framework. Each framework package up above will also re-export everything from this core package. ## Production Builds @@ -67,4 +67,4 @@ npm install -D @tanstack/devtools-vite ``` -Read more about using the devtools in production in our [Production docs](./production.md). \ No newline at end of file +Read more about using the devtools in production in our [Production docs](./production). \ No newline at end of file diff --git a/docs/quick-start.md b/docs/quick-start.md index 91f6f7b4..016c430b 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -225,4 +225,4 @@ export default { } ``` -For the full list of Vite plugin options, see the [Vite Plugin](./vite-plugin.md) documentation. +For the full list of Vite plugin options, see the [Vite Plugin](./vite-plugin) documentation. From df6f01de945da8c34f0bddeca9cb12183d2d8297 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 12:55:14 +0100 Subject: [PATCH 19/21] docs: remove internal plan files --- docs/plans/2026-03-04-documentation-design.md | 300 ------- ...2026-03-04-documentation-implementation.md | 814 ------------------ 2 files changed, 1114 deletions(-) delete mode 100644 docs/plans/2026-03-04-documentation-design.md delete mode 100644 docs/plans/2026-03-04-documentation-implementation.md diff --git a/docs/plans/2026-03-04-documentation-design.md b/docs/plans/2026-03-04-documentation-design.md deleted file mode 100644 index e99b2862..00000000 --- a/docs/plans/2026-03-04-documentation-design.md +++ /dev/null @@ -1,300 +0,0 @@ -# TanStack Devtools Documentation Redesign - -**Date:** 2026-03-04 -**Status:** Approved - -## Goals - -1. Serve both end users (app developers using devtools) and plugin authors (building custom devtools) equally -2. Add a conceptual backbone — architecture, event system, plugin lifecycle -3. Fill framework gaps — full Vue documentation, Solid custom plugins guide -4. Keep everything in the existing `docs/` directory for tanstack.com rendering -5. Fix existing issues (TODOs, missing frameworks in quick-start, thin content) - -## Approach - -**Layered Architecture** — progressive disclosure from "get running in 2 minutes" to "build your own devtools from scratch." - -## Documentation Site Map - -### Getting Started (existing section, updated) - -| Page | Status | Changes | -|------|--------|---------| -| Overview | UPDATE | Add package map, architecture diagram, "What's in the box" section | -| Quick Start | UPDATE | Add Vue, Preact examples alongside React/Solid | -| Installation | UPDATE | Add Vue, Preact install instructions | -| Configuration | MINOR | No major changes | -| Plugin Configuration | MINOR | No major changes | -| Vite Plugin | UPDATE | Add feature explanations for source inspector, console piping | -| Production | KEEP | Good as-is | -| Third-party Plugins | MOVE | Move to Guides section | - -### Getting Started — Framework Pages - -| Page | Status | -|------|--------| -| React: Basic Setup | KEEP | -| React: Adapter | UPDATE — replace "TODO" with actual hooks/exports content | -| Preact: Basic Setup | KEEP | -| Preact: Adapter | KEEP | -| Solid: Basic Setup | KEEP | -| Solid: Adapter | KEEP | -| **Vue: Basic Setup** | **NEW** | -| **Vue: Adapter** | **NEW** | - -### Concepts (NEW section) - -| Page | Description | -|------|-------------| -| **Architecture Overview** | Package map, dependency graph, 3-layer model (transport/core/framework), data flow trace | -| **Event System** | EventClient, typed event maps, emit/on/onAll, plugin scoping, connection lifecycle, server bus, debugging | -| **Plugin Lifecycle** | Plugin interface, mount sequence, DOM containers, theme propagation, framework adapter pattern, cleanup | -| **Source Inspector** | How go-to-source works, data-tsd-source attributes, Vite integration, editor configuration | - -### Guides (existing section, expanded) - -| Page | Status | -|------|--------| -| **Building Custom Plugins** | **NEW** — comprehensive framework-agnostic guide | -| **Using devtools-utils** | **NEW** — plugin factory helpers (createReactPlugin, createSolidPlugin, etc.) | -| **Bidirectional Communication** | **NEW** — app-to-devtools and devtools-to-app patterns, time-travel example | -| Third-party Plugins | MOVED from Getting Started | -| React: Custom Plugins | KEEP | -| Preact: Custom Plugins | KEEP | -| **Solid: Custom Plugins** | **NEW** | -| **Vue: Custom Plugins** | **NEW** | - -### API Reference (existing, minor additions) - -| Page | Status | -|------|--------| -| Core API Reference | KEEP (auto-generated) | -| React Reference | KEEP | -| Preact Reference | KEEP | -| Solid Reference | KEEP | -| **Vue Reference** | **NEW** (auto-generated) | - -### Examples (existing, Vue addition) - -| Page | Status | -|------|--------| -| React: Basic, Start, Custom | KEEP | -| Preact: Basic, Custom | KEEP | -| Solid: Basic | KEEP | -| **Vue: Basic** | **NEW** | - -## New Page Content Designs - -### Architecture Overview (`docs/architecture.md`) - -1. **Package Map** — all 11 packages with 1-line descriptions -2. **Dependency Graph** — visual showing: - ``` - Transport: event-bus ← event-bus-client ← devtools-client - Core: devtools (uses devtools-client, event-bus/client, devtools-ui) - Framework: react-devtools, vue-devtools, solid-devtools, preact-devtools (wrap devtools) - Build: devtools-vite (uses devtools-client, event-bus/server) - Utilities: devtools-utils (uses devtools-ui) - ``` -3. **3-Layer Model**: - - Transport layer: event-bus (WebSocket/SSE server + client), event-bus-client (EventClient abstraction) - - Core layer: devtools (Solid.js shell), devtools-ui (components), devtools-client (core events) - - Framework layer: thin wrappers converting framework JSX to DOM render functions -4. **Data Flow Trace** — from `eventClient.emit('my-event', data)` through to devtools panel rendering - -### Event System (`docs/event-system.md`) - -1. EventClient basics — creating a typed client, emit(), on(), onAll() -2. Event Maps — TypeScript generics for type-safe events -3. Plugin scoping — pluginId prefixes, event filtering -4. Connection lifecycle — queue → connect → retry -5. Server event bus — when needed (Vite integration), when not -6. Debugging — debug mode, console output format - -### Plugin Lifecycle (`docs/plugin-lifecycle.md`) - -1. Plugin interface — name, render, destroy, defaultOpen, id -2. Mount sequence — TanStackDevtoolsCore.mount() call chain -3. DOM containers — PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID -4. Theme propagation — 'light'/'dark' passed to render functions -5. Framework adapter pattern — how React/Vue/Solid convert JSX to DOM -6. Cleanup — destroy() callback timing - -### Source Inspector (`docs/source-inspector.md`) - -1. What it does — click any element to jump to source -2. How it works — data-tsd-source attributes injected by Vite plugin -3. Hotkey configuration — inspectHotkey setting -4. Editor integration — launch-editor, custom editor configuration -5. Ignore patterns — files and components to exclude - -### Building Custom Plugins (`docs/building-custom-plugins.md`) - -1. Overview — what a custom plugin is, when to build one -2. Define your event map — TypeScript types -3. Create an EventClient — extending the base class -4. Emit events from your library — integration patterns -5. Build the devtools panel — consuming events, rendering UI -6. Register the plugin — hooking into TanStackDevtools -7. Advanced: Bidirectional communication -8. Advanced: Using devtools-utils factories -9. Advanced: Framework-agnostic plugins -10. Publishing to the marketplace - -### Using devtools-utils (`docs/devtools-utils.md`) - -1. What devtools-utils provides — factory helpers per framework -2. createReactPlugin() — usage, DevtoolsPanelProps, NoOp fallback -3. createSolidPlugin() — usage, DevtoolsSolidClass -4. createVuePlugin() — usage -5. createPreactPlugin() — usage -6. When to use factories vs manual plugin creation - -### Bidirectional Communication (`docs/bidirectional-communication.md`) - -1. Pattern: App emits state, devtools listens -2. Pattern: Devtools sends commands, app listens -3. Pattern: Time-travel (snapshot collection, revert) -4. Complete example with both directions - -### Vue Pages - -**Vue Basic Setup** — mirrors React basic-setup but with Vue template syntax, script setup, and npm install commands -**Vue Adapter** — documents TanStackDevtoolsVuePlugin interface (component instead of render), TanStackDevtoolsVueInit -**Vue Custom Plugins** — same EventClient setup (framework-agnostic), Vue-specific panel component - -### Updated Pages - -**Overview** — add "What's in the box" package list, architecture diagram, links to Concepts -**Quick Start** — add Vue and Preact tabs/sections -**Installation** — add Vue and Preact install commands -**React Adapter** — replace TODO with actual exported types, component props, usage patterns - -## config.json Changes - -The new `config.json` sections array will be: - -```json -[ - { - "label": "Getting Started", - "children": [ - { "label": "Overview", "to": "overview" }, - { "label": "Quick Start", "to": "quick-start" }, - { "label": "Installation", "to": "installation" }, - { "label": "Configuration", "to": "configuration" }, - { "label": "Plugin Configuration", "to": "plugin-configuration" }, - { "label": "Vite Plugin", "to": "vite-plugin" }, - { "label": "Production", "to": "production" } - ], - "frameworks": [ - { "label": "react", "children": [ - { "label": "Basic Setup", "to": "framework/react/basic-setup" }, - { "label": "React Adapter", "to": "framework/react/adapter" } - ]}, - { "label": "preact", "children": [ - { "label": "Basic Setup", "to": "framework/preact/basic-setup" }, - { "label": "Preact Adapter", "to": "framework/preact/adapter" } - ]}, - { "label": "solid", "children": [ - { "label": "Basic Setup", "to": "framework/solid/basic-setup" }, - { "label": "Solid Adapter", "to": "framework/solid/adapter" } - ]}, - { "label": "vue", "children": [ - { "label": "Basic Setup", "to": "framework/vue/basic-setup" }, - { "label": "Vue Adapter", "to": "framework/vue/adapter" } - ]} - ] - }, - { - "label": "Concepts", - "children": [ - { "label": "Architecture Overview", "to": "architecture" }, - { "label": "Event System", "to": "event-system" }, - { "label": "Plugin Lifecycle", "to": "plugin-lifecycle" }, - { "label": "Source Inspector", "to": "source-inspector" } - ] - }, - { - "label": "Guides", - "children": [ - { "label": "Building Custom Plugins", "to": "building-custom-plugins" }, - { "label": "Using devtools-utils", "to": "devtools-utils" }, - { "label": "Bidirectional Communication", "to": "bidirectional-communication" }, - { "label": "Third-party Plugins", "to": "third-party-plugins" } - ], - "frameworks": [ - { "label": "react", "children": [ - { "label": "Custom Plugins", "to": "framework/react/guides/custom-plugins" } - ]}, - { "label": "preact", "children": [ - { "label": "Custom Plugins", "to": "framework/preact/guides/custom-plugins" } - ]}, - { "label": "solid", "children": [ - { "label": "Custom Plugins", "to": "framework/solid/guides/custom-plugins" } - ]}, - { "label": "vue", "children": [ - { "label": "Custom Plugins", "to": "framework/vue/guides/custom-plugins" } - ]} - ] - }, - { - "label": "API Reference", - "children": [ - { "label": "Core API Reference", "to": "reference/index" } - ], - "frameworks": [ - { "label": "react", "children": [ - { "label": "React Reference", "to": "framework/react/reference/index" } - ]}, - { "label": "preact", "children": [ - { "label": "Preact Reference", "to": "framework/preact/reference/index" } - ]}, - { "label": "solid", "children": [ - { "label": "Solid Reference", "to": "framework/solid/reference/index" } - ]}, - { "label": "vue", "children": [ - { "label": "Vue Reference", "to": "framework/vue/reference/index" } - ]} - ] - }, - { - "label": "Examples", - "children": [], - "frameworks": [ - { "label": "react", "children": [ - { "label": "Basic", "to": "framework/react/examples/basic" }, - { "label": "TanStack Start", "to": "framework/react/examples/start" }, - { "label": "Custom Devtools", "to": "framework/react/examples/custom-devtools" } - ]}, - { "label": "preact", "children": [ - { "label": "Basic", "to": "framework/preact/examples/basic" }, - { "label": "Custom Devtools", "to": "framework/preact/examples/custom-devtools" } - ]}, - { "label": "solid", "children": [ - { "label": "Basic", "to": "framework/solid/examples/basic" } - ]}, - { "label": "vue", "children": [ - { "label": "Basic", "to": "framework/vue/examples/basic" } - ]} - ] - } -] -``` - -## Summary of Changes - -**New pages:** 11 -- Concepts: architecture, event-system, plugin-lifecycle, source-inspector (4) -- Guides: building-custom-plugins, devtools-utils, bidirectional-communication (3) -- Framework: vue/basic-setup, vue/adapter, vue/guides/custom-plugins, solid/guides/custom-plugins (4) - -**Updated pages:** 5 -- overview, quick-start, installation, vite-plugin, framework/react/adapter - -**Moved pages:** 1 -- third-party-plugins (from Getting Started to Guides) - -**Unchanged pages:** ~15 -- configuration, plugin-configuration, production, all existing framework basic-setup/adapter pages, existing custom-plugins guides, API reference, examples diff --git a/docs/plans/2026-03-04-documentation-implementation.md b/docs/plans/2026-03-04-documentation-implementation.md deleted file mode 100644 index 70a36623..00000000 --- a/docs/plans/2026-03-04-documentation-implementation.md +++ /dev/null @@ -1,814 +0,0 @@ -# Documentation Redesign Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Rewrite and expand TanStack Devtools documentation with architecture concepts, Vue support, custom plugin guides, and restructured navigation. - -**Architecture:** Layered docs approach — Getting Started (setup), Concepts (architecture/events/plugins), Guides (building plugins), API Reference, Examples. All files in `docs/` with TanStack docs config.json navigation. - -**Tech Stack:** Markdown with YAML frontmatter, TanStack docs config.json schema - ---- - -### Task 1: Update config.json with new navigation structure - -**Files:** -- Modify: `docs/config.json` - -**Step 1: Replace config.json with the new navigation structure** - -```json -{ - "$schema": "https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json", - "docSearch": { - "appId": "", - "apiKey": "", - "indexName": "tanstack-devtools" - }, - "sections": [ - { - "label": "Getting Started", - "children": [ - { "label": "Overview", "to": "overview" }, - { "label": "Quick Start", "to": "quick-start" }, - { "label": "Installation", "to": "installation" }, - { "label": "Configuration", "to": "configuration" }, - { "label": "Plugin Configuration", "to": "plugin-configuration" }, - { "label": "Vite Plugin", "to": "vite-plugin" }, - { "label": "Production", "to": "production" } - ], - "frameworks": [ - { - "label": "react", - "children": [ - { "label": "Basic Setup", "to": "framework/react/basic-setup" }, - { "label": "React Adapter", "to": "framework/react/adapter" } - ] - }, - { - "label": "preact", - "children": [ - { "label": "Basic Setup", "to": "framework/preact/basic-setup" }, - { "label": "Preact Adapter", "to": "framework/preact/adapter" } - ] - }, - { - "label": "solid", - "children": [ - { "label": "Basic Setup", "to": "framework/solid/basic-setup" }, - { "label": "Solid Adapter", "to": "framework/solid/adapter" } - ] - }, - { - "label": "vue", - "children": [ - { "label": "Basic Setup", "to": "framework/vue/basic-setup" }, - { "label": "Vue Adapter", "to": "framework/vue/adapter" } - ] - } - ] - }, - { - "label": "Concepts", - "children": [ - { "label": "Architecture Overview", "to": "architecture" }, - { "label": "Event System", "to": "event-system" }, - { "label": "Plugin Lifecycle", "to": "plugin-lifecycle" }, - { "label": "Source Inspector", "to": "source-inspector" } - ] - }, - { - "label": "Guides", - "children": [ - { "label": "Building Custom Plugins", "to": "building-custom-plugins" }, - { "label": "Using devtools-utils", "to": "devtools-utils" }, - { "label": "Bidirectional Communication", "to": "bidirectional-communication" }, - { "label": "Third-party Plugins", "to": "third-party-plugins" } - ], - "frameworks": [ - { - "label": "react", - "children": [ - { "label": "Custom Plugins", "to": "framework/react/guides/custom-plugins" } - ] - }, - { - "label": "preact", - "children": [ - { "label": "Custom Plugins", "to": "framework/preact/guides/custom-plugins" } - ] - }, - { - "label": "solid", - "children": [ - { "label": "Custom Plugins", "to": "framework/solid/guides/custom-plugins" } - ] - }, - { - "label": "vue", - "children": [ - { "label": "Custom Plugins", "to": "framework/vue/guides/custom-plugins" } - ] - } - ] - }, - { - "label": "API Reference", - "children": [ - { "label": "Core API Reference", "to": "reference/index" } - ], - "frameworks": [ - { - "label": "react", - "children": [ - { "label": "React Reference", "to": "framework/react/reference/index" } - ] - }, - { - "label": "preact", - "children": [ - { "label": "Preact Reference", "to": "framework/preact/reference/index" } - ] - }, - { - "label": "solid", - "children": [ - { "label": "Solid Reference", "to": "framework/solid/reference/index" } - ] - }, - { - "label": "vue", - "children": [ - { "label": "Vue Reference", "to": "framework/vue/reference/index" } - ] - } - ] - }, - { - "label": "Examples", - "children": [], - "frameworks": [ - { - "label": "react", - "children": [ - { "label": "Basic", "to": "framework/react/examples/basic" }, - { "label": "TanStack Start", "to": "framework/react/examples/start" }, - { "label": "Custom Devtools", "to": "framework/react/examples/custom-devtools" } - ] - }, - { - "label": "preact", - "children": [ - { "label": "Basic", "to": "framework/preact/examples/basic" }, - { "label": "Custom Devtools", "to": "framework/preact/examples/custom-devtools" } - ] - }, - { - "label": "solid", - "children": [ - { "label": "Basic", "to": "framework/solid/examples/basic" } - ] - }, - { - "label": "vue", - "children": [ - { "label": "Basic", "to": "framework/vue/examples/basic" } - ] - } - ] - } - ] -} -``` - -**Step 2: Commit** - -```bash -git add docs/config.json -git commit -m "docs: restructure navigation with Concepts and Guides sections, add Vue" -``` - ---- - -### Task 2: Rewrite Overview page - -**Files:** -- Modify: `docs/overview.md` - -**Step 1: Rewrite overview.md with package map and architecture diagram** - -The updated overview should contain: -1. Keep the existing intro paragraph and origin story -2. Add a "What's in the Box" section listing all packages grouped by layer: - - **Framework Adapters**: `@tanstack/react-devtools`, `@tanstack/vue-devtools`, `@tanstack/solid-devtools`, `@tanstack/preact-devtools` - - **Core**: `@tanstack/devtools` — the shell UI, plugin system, settings - - **Event System**: `@tanstack/devtools-event-client` — typed event client for plugins; `@tanstack/devtools-event-bus` — WebSocket/SSE transport - - **Build Tools**: `@tanstack/devtools-vite` — Vite plugin for source inspection, console piping, production stripping - - **Utilities**: `@tanstack/devtools-utils` — plugin factory helpers; `@tanstack/devtools-ui` — shared UI components; `@tanstack/devtools-client` — core devtools events -3. Add a text-based architecture diagram showing the 3 layers -4. Update the Key Features list to include: Framework Agnostic, Plugin Marketplace, Type-Safe Event System, Source Inspector, Console Piping, Picture-in-Picture mode -5. Add "Next Steps" links to Quick Start and Architecture Overview - -**Step 2: Commit** - -```bash -git add docs/overview.md -git commit -m "docs: expand overview with package map and architecture diagram" -``` - ---- - -### Task 3: Update Quick Start to cover all frameworks - -**Files:** -- Modify: `docs/quick-start.md` - -**Step 1: Expand quick-start.md with all 4 frameworks** - -Add install commands and code examples for all 4 frameworks. The existing React content stays. Add: - -- **Preact** section: `npm install @tanstack/preact-devtools @tanstack/devtools-vite`, with `import { TanStackDevtools } from '@tanstack/preact-devtools'` and Preact render pattern -- **Solid** section: `npm install @tanstack/solid-devtools @tanstack/devtools-vite`, with Solid render pattern -- **Vue** section: `npm install @tanstack/vue-devtools`, with ` - - - ``` -3. With plugins (Vue Query example): - ```vue - - - - ``` -4. Note the key difference: Vue uses `component` instead of `render` in plugin definitions. This is because Vue components are passed as component references, not JSX elements. -5. Link to configuration docs and the Vue basic example. - -**Step 2: Commit** - -```bash -git add docs/framework/vue/basic-setup.md -git commit -m "docs: add Vue basic setup page" -``` - ---- - -### Task 13: Create Vue Adapter page - -**Files:** -- Create: `docs/framework/vue/adapter.md` - -**Step 1: Write adapter.md** - -Frontmatter: `title: TanStack Devtools Vue Adapter`, `id: adapter` - -Content: -1. **Overview** — The Vue adapter wraps `TanStackDevtoolsCore` in a Vue 3 component. It uses Vue's `` to render plugin components into the devtools' DOM containers. -2. **Installation** — `npm install @tanstack/vue-devtools` -3. **Component Props** — `TanStackDevtoolsVueInit` interface: - - `plugins?: TanStackDevtoolsVuePlugin[]` — Array of plugins - - `config?: Partial` — Devtools configuration - - `eventBusConfig?: ClientEventBusConfig` — Event bus configuration -4. **Plugin Type** — `TanStackDevtoolsVuePlugin`: - - `id?: string` — Unique identifier - - `component: Component` — Vue component to render in the panel - - `name: string | Component` — Display name or Vue component for the tab title - - `props?: Record` — Additional props passed to the component -5. **Key Difference from Other Adapters** — Vue plugins use `component` (a Vue component reference) instead of `render` (a JSX element). Props are passed via the `props` field and are bound with `v-bind`. -6. **Exports** — `TanStackDevtools` (component), `TanStackDevtoolsVuePlugin` (type), `TanStackDevtoolsVueInit` (type). Also re-exports everything from `@tanstack/devtools`. - -**Step 2: Commit** - -```bash -git add docs/framework/vue/adapter.md -git commit -m "docs: add Vue adapter page" -``` - ---- - -### Task 14: Create Vue Custom Plugins guide - -**Files:** -- Create: `docs/framework/vue/guides/custom-plugins.md` - -**Step 1: Write custom-plugins.md** - -Frontmatter: `title: Custom plugins`, `id: custom-plugins` - -Mirrors the React custom-plugins guide but with Vue syntax. Same counter example. - -Key differences: -- DevtoolsPanel is a Vue component using `