From edfbd43b032e3d54544a5e4de37f10990a059ec4 Mon Sep 17 00:00:00 2001
From: Kevin Van Cott
Date: Sat, 6 Dec 2025 11:07:52 -0600
Subject: [PATCH 1/4] feat: add preact devtools
---
docs/config.json | 44 +++
docs/framework/preact/adapter.md | 16 +
docs/framework/preact/basic-setup.md | 69 ++++
.../framework/preact/guides/custom-plugins.md | 204 +++++++++++
.../reference/functions/tanstackdevtools.md | 24 --
docs/framework/solid/reference/index.md | 16 -
.../tanstackdevtoolssolidplugin.md | 63 ----
.../reference/classes/tanstackdevtoolscore.md | 14 +-
.../interfaces/tanstackdevtoolsinit.md | 11 +-
.../interfaces/tanstackdevtoolsplugin.md | 55 ++-
.../type-aliases/tanstackdevtoolsconfig.md | 2 +-
examples/preact/basic/.eslintrc.cjs | 13 +
examples/preact/basic/.gitignore | 27 ++
examples/preact/basic/README.md | 6 +
examples/preact/basic/index.html | 40 +++
examples/preact/basic/package.json | 38 ++
examples/preact/basic/public/emblem-light.svg | 13 +
.../basic/src/button-with-props-only.tsx | 5 +
examples/preact/basic/src/button.tsx | 3 +
examples/preact/basic/src/feature.tsx | 12 +
examples/preact/basic/src/index.tsx | 190 ++++++++++
.../preact/basic/src/package-json-panel.tsx | 340 ++++++++++++++++++
examples/preact/basic/src/plugin.ts | 25 ++
examples/preact/basic/src/setup.tsx | 20 ++
examples/preact/basic/tsconfig.json | 24 ++
examples/preact/basic/vite.config.ts | 21 ++
examples/preact/custom-devtools/.eslintrc.cjs | 11 +
examples/preact/custom-devtools/.gitignore | 27 ++
examples/preact/custom-devtools/README.md | 6 +
examples/preact/custom-devtools/index.html | 16 +
examples/preact/custom-devtools/package.json | 32 ++
.../custom-devtools/public/emblem-light.svg | 13 +
examples/preact/custom-devtools/src/App.tsx | 30 ++
.../src/CustomDevtoolsPanel.tsx | 23 ++
.../preact/custom-devtools/src/counter.ts | 29 ++
.../preact/custom-devtools/src/eventClient.ts | 20 ++
examples/preact/custom-devtools/src/index.tsx | 22 ++
examples/preact/custom-devtools/tsconfig.json | 24 ++
.../preact/custom-devtools/vite.config.ts | 7 +
package.json | 1 +
packages/devtools-utils/package.json | 16 +-
packages/devtools-utils/src/preact/index.ts | 3 +
packages/devtools-utils/src/preact/panel.tsx | 60 ++++
packages/devtools-utils/src/preact/plugin.tsx | 31 ++
packages/devtools-utils/tsconfig.json | 1 +
packages/devtools-utils/vite.config.preact.ts | 26 ++
packages/preact-devtools/CHANGELOG.md | 8 +
packages/preact-devtools/README.md | 58 +++
packages/preact-devtools/eslint.config.js | 19 +
packages/preact-devtools/package.json | 62 ++++
packages/preact-devtools/src/devtools.tsx | 308 ++++++++++++++++
packages/preact-devtools/src/index.ts | 11 +
packages/preact-devtools/tsconfig.docs.json | 10 +
packages/preact-devtools/tsconfig.json | 9 +
packages/preact-devtools/vite.config.ts | 27 ++
packages/react-devtools/package.json | 2 +-
pnpm-lock.yaml | 252 ++++++++++++-
vitest.workspace.js | 1 +
58 files changed, 2329 insertions(+), 131 deletions(-)
create mode 100644 docs/framework/preact/adapter.md
create mode 100644 docs/framework/preact/basic-setup.md
create mode 100644 docs/framework/preact/guides/custom-plugins.md
delete mode 100644 docs/framework/solid/reference/functions/tanstackdevtools.md
delete mode 100644 docs/framework/solid/reference/index.md
delete mode 100644 docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md
create mode 100644 examples/preact/basic/.eslintrc.cjs
create mode 100644 examples/preact/basic/.gitignore
create mode 100644 examples/preact/basic/README.md
create mode 100644 examples/preact/basic/index.html
create mode 100644 examples/preact/basic/package.json
create mode 100644 examples/preact/basic/public/emblem-light.svg
create mode 100644 examples/preact/basic/src/button-with-props-only.tsx
create mode 100644 examples/preact/basic/src/button.tsx
create mode 100644 examples/preact/basic/src/feature.tsx
create mode 100644 examples/preact/basic/src/index.tsx
create mode 100644 examples/preact/basic/src/package-json-panel.tsx
create mode 100644 examples/preact/basic/src/plugin.ts
create mode 100644 examples/preact/basic/src/setup.tsx
create mode 100644 examples/preact/basic/tsconfig.json
create mode 100644 examples/preact/basic/vite.config.ts
create mode 100644 examples/preact/custom-devtools/.eslintrc.cjs
create mode 100644 examples/preact/custom-devtools/.gitignore
create mode 100644 examples/preact/custom-devtools/README.md
create mode 100644 examples/preact/custom-devtools/index.html
create mode 100644 examples/preact/custom-devtools/package.json
create mode 100644 examples/preact/custom-devtools/public/emblem-light.svg
create mode 100644 examples/preact/custom-devtools/src/App.tsx
create mode 100644 examples/preact/custom-devtools/src/CustomDevtoolsPanel.tsx
create mode 100644 examples/preact/custom-devtools/src/counter.ts
create mode 100644 examples/preact/custom-devtools/src/eventClient.ts
create mode 100644 examples/preact/custom-devtools/src/index.tsx
create mode 100644 examples/preact/custom-devtools/tsconfig.json
create mode 100644 examples/preact/custom-devtools/vite.config.ts
create mode 100644 packages/devtools-utils/src/preact/index.ts
create mode 100644 packages/devtools-utils/src/preact/panel.tsx
create mode 100644 packages/devtools-utils/src/preact/plugin.tsx
create mode 100644 packages/devtools-utils/vite.config.preact.ts
create mode 100644 packages/preact-devtools/CHANGELOG.md
create mode 100644 packages/preact-devtools/README.md
create mode 100644 packages/preact-devtools/eslint.config.js
create mode 100644 packages/preact-devtools/package.json
create mode 100644 packages/preact-devtools/src/devtools.tsx
create mode 100644 packages/preact-devtools/src/index.ts
create mode 100644 packages/preact-devtools/tsconfig.docs.json
create mode 100644 packages/preact-devtools/tsconfig.json
create mode 100644 packages/preact-devtools/vite.config.ts
diff --git a/docs/config.json b/docs/config.json
index b0053346..e03fc3c7 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -56,6 +56,19 @@
}
]
},
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "Basic setup",
+ "to": "framework/preact/basic-setup"
+ },
+ {
+ "label": "Preact adapter",
+ "to": "framework/preact/adapter"
+ }
+ ]
+ },
{
"label": "solid",
"children": [
@@ -84,6 +97,15 @@
}
]
},
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "Custom plugins",
+ "to": "framework/preact/guides/custom-plugins"
+ }
+ ]
+ },
{
"label": "solid",
"children": []
@@ -108,6 +130,15 @@
}
]
},
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "Preact Hooks",
+ "to": "framework/preact/reference/index"
+ }
+ ]
+ },
{
"label": "solid",
"children": [
@@ -140,6 +171,19 @@
}
]
},
+ {
+ "label": "preact",
+ "children": [
+ {
+ "label": "Basic",
+ "to": "framework/preact/examples/basic"
+ },
+ {
+ "label": "Custom devtools",
+ "to": "framework/preact/examples/custom-devtools"
+ }
+ ]
+ },
{
"label": "solid",
"children": [
diff --git a/docs/framework/preact/adapter.md b/docs/framework/preact/adapter.md
new file mode 100644
index 00000000..e490fa97
--- /dev/null
+++ b/docs/framework/preact/adapter.md
@@ -0,0 +1,16 @@
+---
+title: TanStack Devtools Preact Adapter
+id: adapter
+---
+
+If you are using TanStack Devtools in a Preact application, we recommend using the Preact Adapter. The Preact Adapter provides a set of easy-to-use hooks on top of the core Devtools utilities. If you find yourself wanting to use the core Devtools classes/functions directly, the Preact Adapter will also re-export everything from the core package.
+
+## Installation
+
+```sh
+npm install @tanstack/preact-devtools
+```
+
+## Preact Hooks
+
+TODO
diff --git a/docs/framework/preact/basic-setup.md b/docs/framework/preact/basic-setup.md
new file mode 100644
index 00000000..efb501b6
--- /dev/null
+++ b/docs/framework/preact/basic-setup.md
@@ -0,0 +1,69 @@
+---
+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/preact-devtools) library, this will install the devtools core as well as provide you framework specific adapters.
+
+```bash
+npm i @tanstack/preact-devtools
+```
+
+Next in the root of your application import the `TanStackDevtools` from the required framework adapter (in this case @tanstack/preact-devtools).
+
+```tsx
+import { TanStackDevtools } from '@tanstack/preact-devtools'
+import { render } from 'preact'
+
+import App from './App'
+
+render(
+ <>
+
+
+
+ >,
+ document.getElementById('root')!,
+)
+```
+
+Import the desired devtools and provide it to the `TanStackDevtools` component along with a label for the menu.
+
+Currently TanStack offers:
+
+- `QueryDevtools`
+- `RouterDevtools`
+- `PacerDevtools`
+- `FormDevtools` [coming soon](https://github.com/TanStack/form/pull/1692)
+
+```tsx
+import { render } from 'preact'
+
+import { TanStackDevtools } from '@tanstack/preact-devtools'
+
+import App from './App'
+
+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.
+
+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
new file mode 100644
index 00000000..2bf63cf6
--- /dev/null
+++ b/docs/framework/preact/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/preact/examples/custom-plugin).
+
+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).
+
+DevtoolsPanel.tsx
+```tsx
+import { useEffect, useState } from 'preact/hooks'
+import { DevtoolsEventClient } from './eventClient.ts'
+
+export function DevtoolPanel() {
+ const [state, setState] = useState<{ count: number; history: number[] } | undefined>()
+
+ useEffect(() => {
+ // subscribe to the emitted event
+ const cleanup = DevtoolsEventClient.on("counter-state", e => setState(e.payload))
+ return 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. 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
+import { render } from 'preact'
+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/solid/reference/functions/tanstackdevtools.md b/docs/framework/solid/reference/functions/tanstackdevtools.md
deleted file mode 100644
index 6ef8a24b..00000000
--- a/docs/framework/solid/reference/functions/tanstackdevtools.md
+++ /dev/null
@@ -1,24 +0,0 @@
----
-id: TanStackDevtools
-title: TanStackDevtools
----
-
-
-
-# Function: TanStackDevtools()
-
-```ts
-function TanStackDevtools(props): any
-```
-
-Defined in: [index.ts:4](https://github.com/TanStack/devtools/blob/main/packages/solid-devtools/src/index.ts#L4)
-
-## Parameters
-
-### props
-
-`TanStackDevtoolsInit` & `object`
-
-## Returns
-
-`any`
diff --git a/docs/framework/solid/reference/index.md b/docs/framework/solid/reference/index.md
deleted file mode 100644
index ba505f55..00000000
--- a/docs/framework/solid/reference/index.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-id: "@tanstack/solid-devtools"
-title: "@tanstack/solid-devtools"
----
-
-
-
-# @tanstack/solid-devtools
-
-## Type Aliases
-
-- [TanStackDevtoolsSolidPlugin](type-aliases/tanstackdevtoolssolidplugin.md)
-
-## Functions
-
-- [TanStackDevtools](functions/tanstackdevtools.md)
diff --git a/docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md b/docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md
deleted file mode 100644
index 86e6ad8b..00000000
--- a/docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md
+++ /dev/null
@@ -1,63 +0,0 @@
----
-id: TanStackDevtoolsSolidPlugin
-title: TanStackDevtoolsSolidPlugin
----
-
-
-
-# Type Alias: TanStackDevtoolsSolidPlugin
-
-```ts
-type TanStackDevtoolsSolidPlugin = Omit & object;
-```
-
-Defined in: [core.tsx:21](https://github.com/TanStack/devtools/blob/main/packages/solid-devtools/src/core.tsx#L21)
-
-## Type declaration
-
-### name
-
-```ts
-name: string | SolidPluginRender;
-```
-
-Name to be displayed in the devtools UI.
-If a string, it will be used as the plugin name.
-If a function, it will be called with the mount element.
-
-Example:
-```ts
- {
- name: "Your Plugin",
- render: () => ,
- }
-```
-or
-```ts
- {
- name:
Your Plugin title
,
- render: () => ,
- }
-```
-
-### render
-
-```ts
-render: SolidPluginRender;
-```
-
-The render function can be a SolidJS element or a function that returns a SolidJS element.
-If it's a function, it will be called to render the plugin, otherwise it will be rendered directly.
-
-Example:
-```ts
- {
- render: () => ,
- }
-```
-or
-```ts
- {
- render: ,
- }
-```
diff --git a/docs/reference/classes/tanstackdevtoolscore.md b/docs/reference/classes/tanstackdevtoolscore.md
index 7c93c312..9152cb07 100644
--- a/docs/reference/classes/tanstackdevtoolscore.md
+++ b/docs/reference/classes/tanstackdevtoolscore.md
@@ -7,7 +7,7 @@ title: TanStackDevtoolsCore
# Class: TanStackDevtoolsCore
-Defined in: [devtools/src/core.tsx:42](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L42)
+Defined in: [devtools/src/core.tsx:43](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L43)
## Constructors
@@ -17,13 +17,13 @@ Defined in: [devtools/src/core.tsx:42](https://github.com/TanStack/devtools/blob
new TanStackDevtoolsCore(init): TanStackDevtoolsCore
```
-Defined in: [devtools/src/core.tsx:53](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L53)
+Defined in: [devtools/src/core.tsx:55](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L55)
#### Parameters
##### init
-[`TanStackDevtoolsInit`](./interfaces/tanstackdevtoolsinit.md)
+[`TanStackDevtoolsInit`](../interfaces/tanstackdevtoolsinit.md)
#### Returns
@@ -37,7 +37,7 @@ Defined in: [devtools/src/core.tsx:53](https://github.com/TanStack/devtools/blob
mount(el): void
```
-Defined in: [devtools/src/core.tsx:62](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L62)
+Defined in: [devtools/src/core.tsx:64](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L64)
#### Type Parameters
@@ -61,13 +61,13 @@ Defined in: [devtools/src/core.tsx:62](https://github.com/TanStack/devtools/blob
setConfig(config): void
```
-Defined in: [devtools/src/core.tsx:94](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L94)
+Defined in: [devtools/src/core.tsx:107](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L107)
#### Parameters
##### config
-`Partial`\<[`TanStackDevtoolsInit`](./interfaces/tanstackdevtoolsinit.md)\>
+`Partial`\<[`TanStackDevtoolsInit`](../interfaces/tanstackdevtoolsinit.md)\>
#### Returns
@@ -81,7 +81,7 @@ Defined in: [devtools/src/core.tsx:94](https://github.com/TanStack/devtools/blob
unmount(): void
```
-Defined in: [devtools/src/core.tsx:85](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L85)
+Defined in: [devtools/src/core.tsx:98](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L98)
#### Returns
diff --git a/docs/reference/interfaces/tanstackdevtoolsinit.md b/docs/reference/interfaces/tanstackdevtoolsinit.md
index b42e757e..eeb4e5f6 100644
--- a/docs/reference/interfaces/tanstackdevtoolsinit.md
+++ b/docs/reference/interfaces/tanstackdevtoolsinit.md
@@ -7,7 +7,7 @@ title: TanStackDevtoolsInit
# Interface: TanStackDevtoolsInit
-Defined in: [devtools/src/core.tsx:12](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L12)
+Defined in: [devtools/src/core.tsx:13](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L13)
## Properties
@@ -15,17 +15,20 @@ Defined in: [devtools/src/core.tsx:12](https://github.com/TanStack/devtools/blob
```ts
optional config: Partial<{
+ customTrigger: (el, props) => void;
defaultOpen: boolean;
hideUntilHover: boolean;
openHotkey: KeyboardKey[];
panelLocation: "top" | "bottom";
position: TriggerPosition;
requireUrlFlag: boolean;
+ theme: "light" | "dark";
+ triggerHidden: boolean;
urlFlag: string;
}>;
```
-Defined in: [devtools/src/core.tsx:18](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L18)
+Defined in: [devtools/src/core.tsx:19](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L19)
Configuration for the devtools shell. These configuration options are used to set the
initial state of the devtools when it is started for the first time. Afterwards,
@@ -39,7 +42,7 @@ the settings are persisted in local storage and changed through the settings pan
optional eventBusConfig: ClientEventBusConfig;
```
-Defined in: [devtools/src/core.tsx:39](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L39)
+Defined in: [devtools/src/core.tsx:40](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L40)
***
@@ -49,7 +52,7 @@ Defined in: [devtools/src/core.tsx:39](https://github.com/TanStack/devtools/blob
optional plugins: TanStackDevtoolsPlugin[];
```
-Defined in: [devtools/src/core.tsx:38](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L38)
+Defined in: [devtools/src/core.tsx:39](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L39)
Array of plugins to be used in the devtools.
Each plugin has a `render` function that gives you the dom node to mount into
diff --git a/docs/reference/interfaces/tanstackdevtoolsplugin.md b/docs/reference/interfaces/tanstackdevtoolsplugin.md
index 919639e2..bce835d0 100644
--- a/docs/reference/interfaces/tanstackdevtoolsplugin.md
+++ b/docs/reference/interfaces/tanstackdevtoolsplugin.md
@@ -7,17 +7,56 @@ title: TanStackDevtoolsPlugin
# Interface: TanStackDevtoolsPlugin
-Defined in: [devtools/src/context/devtools-context.tsx:14](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L14)
+Defined in: [devtools/src/context/devtools-context.tsx:15](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L15)
## Properties
+### defaultOpen?
+
+```ts
+optional defaultOpen: boolean;
+```
+
+Defined in: [devtools/src/context/devtools-context.tsx:58](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L58)
+
+Whether the plugin should be open by default when there are no active plugins.
+If true, this plugin will be added to activePlugins on initial load when activePlugins is empty.
+
+#### Default
+
+```ts
+false
+```
+
+***
+
+### destroy()?
+
+```ts
+optional destroy: (pluginId) => void;
+```
+
+Defined in: [devtools/src/context/devtools-context.tsx:77](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L77)
+
+#### Parameters
+
+##### pluginId
+
+`string`
+
+#### Returns
+
+`void`
+
+***
+
### id?
```ts
optional id: string;
```
-Defined in: [devtools/src/context/devtools-context.tsx:46](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L46)
+Defined in: [devtools/src/context/devtools-context.tsx:52](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L52)
Unique identifier for the plugin.
If not provided, it will be generated based on the name.
@@ -27,10 +66,10 @@ If not provided, it will be generated based on the name.
### name
```ts
-name: string | (el) => void;
+name: string | (el, theme) => void;
```
-Defined in: [devtools/src/context/devtools-context.tsx:41](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L41)
+Defined in: [devtools/src/context/devtools-context.tsx:42](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L42)
Name to be displayed in the devtools UI.
If a string, it will be used as the plugin name.
@@ -62,10 +101,10 @@ or
### render()
```ts
-render: (el) => void;
+render: (el, theme) => void;
```
-Defined in: [devtools/src/context/devtools-context.tsx:60](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L60)
+Defined in: [devtools/src/context/devtools-context.tsx:72](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L72)
Render the plugin UI by using the provided element. This function will be called
when the plugin tab is clicked and expected to be mounted.
@@ -78,6 +117,10 @@ when the plugin tab is clicked and expected to be mounted.
The mount element for the plugin.
+##### theme
+
+`"light"` | `"dark"`
+
#### Returns
`void`
diff --git a/docs/reference/type-aliases/tanstackdevtoolsconfig.md b/docs/reference/type-aliases/tanstackdevtoolsconfig.md
index ff1f00d4..dbe96c72 100644
--- a/docs/reference/type-aliases/tanstackdevtoolsconfig.md
+++ b/docs/reference/type-aliases/tanstackdevtoolsconfig.md
@@ -11,4 +11,4 @@ title: TanStackDevtoolsConfig
type TanStackDevtoolsConfig = DevtoolsStore["settings"];
```
-Defined in: [devtools/src/context/devtools-context.tsx:124](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L124)
+Defined in: [devtools/src/context/devtools-context.tsx:182](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L182)
diff --git a/examples/preact/basic/.eslintrc.cjs b/examples/preact/basic/.eslintrc.cjs
new file mode 100644
index 00000000..9ff0b9fc
--- /dev/null
+++ b/examples/preact/basic/.eslintrc.cjs
@@ -0,0 +1,13 @@
+// @ts-check
+
+/** @type {import('eslint').Linter.Config} */
+const config = {
+ settings: {
+ extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
+ rules: {
+ 'react/no-children-prop': 'off',
+ },
+ },
+}
+
+module.exports = config
diff --git a/examples/preact/basic/.gitignore b/examples/preact/basic/.gitignore
new file mode 100644
index 00000000..4673b022
--- /dev/null
+++ b/examples/preact/basic/.gitignore
@@ -0,0 +1,27 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+pnpm-lock.yaml
+yarn.lock
+package-lock.json
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/examples/preact/basic/README.md b/examples/preact/basic/README.md
new file mode 100644
index 00000000..1cf88926
--- /dev/null
+++ b/examples/preact/basic/README.md
@@ -0,0 +1,6 @@
+# Example
+
+To run this example:
+
+- `npm install`
+- `npm run dev`
diff --git a/examples/preact/basic/index.html b/examples/preact/basic/index.html
new file mode 100644
index 00000000..1c96ec87
--- /dev/null
+++ b/examples/preact/basic/index.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Basic Example - TanStack Devtools
+ A basic example of using TanStack Devtools with React.
+
+
+
+
+
+
+
diff --git a/examples/preact/basic/package.json b/examples/preact/basic/package.json
new file mode 100644
index 00000000..ad8f091b
--- /dev/null
+++ b/examples/preact/basic/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@tanstack/devtools-example-preact-basic",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port=3005",
+ "build": "vite build",
+ "preview": "vite preview",
+ "test:types": "tsc"
+ },
+ "dependencies": {
+ "@tanstack/devtools-client": "0.0.4",
+ "@tanstack/devtools-event-client": "0.3.5",
+ "@tanstack/preact-devtools": "workspace:*",
+ "preact": "^10.28.0",
+ "zod": "^4.1.11"
+ },
+ "devDependencies": {
+ "@preact/preset-vite": "^2.10.2",
+ "@tanstack/devtools-ui": "0.4.4",
+ "@tanstack/devtools-vite": "0.3.11",
+ "sonda": "0.9.0",
+ "vite": "^7.1.7",
+ "vite-plugin-inspect": "11.3.3"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/examples/preact/basic/public/emblem-light.svg b/examples/preact/basic/public/emblem-light.svg
new file mode 100644
index 00000000..a58e69ad
--- /dev/null
+++ b/examples/preact/basic/public/emblem-light.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/examples/preact/basic/src/button-with-props-only.tsx b/examples/preact/basic/src/button-with-props-only.tsx
new file mode 100644
index 00000000..0b79cfee
--- /dev/null
+++ b/examples/preact/basic/src/button-with-props-only.tsx
@@ -0,0 +1,5 @@
+import { Button } from './button'
+
+export const ButtonWithProps = (props: { children: React.ReactNode }) => {
+ return
+}
diff --git a/examples/preact/basic/src/button.tsx b/examples/preact/basic/src/button.tsx
new file mode 100644
index 00000000..528cd438
--- /dev/null
+++ b/examples/preact/basic/src/button.tsx
@@ -0,0 +1,3 @@
+export function Button(props: any) {
+ return
+}
diff --git a/examples/preact/basic/src/feature.tsx b/examples/preact/basic/src/feature.tsx
new file mode 100644
index 00000000..03c65904
--- /dev/null
+++ b/examples/preact/basic/src/feature.tsx
@@ -0,0 +1,12 @@
+import { Button } from './button'
+import { ButtonWithProps } from './button-with-props-only'
+
+export function Feature() {
+ return (
+
+ As you visit the posts below, you will notice them in a loading
+ state the first time you load them. However, after you return to
+ this list and click on any posts you have already visited again, you
+ will see them load instantly and background refresh right before
+ your eyes!{' '}
+
+ (You may need to throttle your network speed to simulate longer
+ loading sequences)
+
+
+ )
+}
diff --git a/examples/preact/custom-devtools/src/counter.ts b/examples/preact/custom-devtools/src/counter.ts
new file mode 100644
index 00000000..9d8db1e0
--- /dev/null
+++ b/examples/preact/custom-devtools/src/counter.ts
@@ -0,0 +1,29 @@
+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,
+ })
+ },
+ }
+}
diff --git a/examples/preact/custom-devtools/src/eventClient.ts b/examples/preact/custom-devtools/src/eventClient.ts
new file mode 100644
index 00000000..d884641d
--- /dev/null
+++ b/examples/preact/custom-devtools/src/eventClient.ts
@@ -0,0 +1,20 @@
+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: Array }
+}
+
+class CustomEventClient extends EventClient {
+ constructor() {
+ super({
+ // The pluginId must match that of the event map key
+ pluginId: 'custom-devtools',
+ debug: true,
+ })
+ }
+}
+
+// This is where the magic happens, it'll be used throughout your application.
+export const DevtoolsEventClient = new CustomEventClient()
diff --git a/examples/preact/custom-devtools/src/index.tsx b/examples/preact/custom-devtools/src/index.tsx
new file mode 100644
index 00000000..1ea17de2
--- /dev/null
+++ b/examples/preact/custom-devtools/src/index.tsx
@@ -0,0 +1,22 @@
+import { render } from 'preact'
+import { TanStackDevtools } from '@tanstack/preact-devtools'
+
+import App from './App'
+import CustomDevtoolPanel from './CustomDevtoolsPanel'
+
+render(
+ <>
+
+
+ ,
+ },
+ ]}
+ />
+ >,
+ document.getElementById('root')!,
+)
diff --git a/examples/preact/custom-devtools/tsconfig.json b/examples/preact/custom-devtools/tsconfig.json
new file mode 100644
index 00000000..ef94db5e
--- /dev/null
+++ b/examples/preact/custom-devtools/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "Bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "jsxImportSource": "preact",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/preact/custom-devtools/vite.config.ts b/examples/preact/custom-devtools/vite.config.ts
new file mode 100644
index 00000000..cb76bf8a
--- /dev/null
+++ b/examples/preact/custom-devtools/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import preact from '@preact/preset-vite'
+
+export default defineConfig({
+ plugins: [preact()],
+})
+
diff --git a/package.json b/package.json
index 9155550d..c8d90d6a 100644
--- a/package.json
+++ b/package.json
@@ -87,6 +87,7 @@
"overrides": {
"@tanstack/devtools": "workspace:*",
"@tanstack/react-devtools": "workspace:*",
+ "@tanstack/preact-devtools": "workspace:*",
"@tanstack/solid-devtools": "workspace:*",
"@tanstack/devtools-vite": "workspace:*"
}
diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json
index 7067a2e9..b0922ed2 100644
--- a/packages/devtools-utils/package.json
+++ b/packages/devtools-utils/package.json
@@ -25,6 +25,12 @@
"default": "./dist/react/esm/index.js"
}
},
+ "./preact": {
+ "import": {
+ "types": "./dist/preact/esm/index.d.ts",
+ "default": "./dist/preact/esm/index.js"
+ }
+ },
"./solid": {
"browser": {
"development": {
@@ -52,15 +58,19 @@
},
"peerDependencies": {
"@types/react": ">=17.0.0",
+ "preact": ">=10.0.0",
"react": ">=17.0.0",
"solid-js": ">=1.9.7",
"vue": ">=3.2.0"
},
"peerDependenciesMeta": {
- "react": {
+ "@types/react": {
"optional": true
},
- "@types/react": {
+ "preact": {
+ "optional": true
+ },
+ "react": {
"optional": true
},
"solid-js": {
@@ -82,7 +92,7 @@
"test:lib:dev": "pnpm test:lib --watch",
"test:types": "tsc",
"test:build": "publint --strict",
- "build": "vite build && tsup "
+ "build": "vite build && vite build --config vite.config.preact.ts && tsup "
},
"devDependencies": {
"tsup": "^8.5.0",
diff --git a/packages/devtools-utils/src/preact/index.ts b/packages/devtools-utils/src/preact/index.ts
new file mode 100644
index 00000000..90646448
--- /dev/null
+++ b/packages/devtools-utils/src/preact/index.ts
@@ -0,0 +1,3 @@
+export * from './panel'
+export * from './plugin'
+
diff --git a/packages/devtools-utils/src/preact/panel.tsx b/packages/devtools-utils/src/preact/panel.tsx
new file mode 100644
index 00000000..3e5af1c0
--- /dev/null
+++ b/packages/devtools-utils/src/preact/panel.tsx
@@ -0,0 +1,60 @@
+/** @jsxImportSource preact */
+
+import { useEffect, useRef } from 'preact/hooks'
+
+export interface DevtoolsPanelProps {
+ theme?: 'light' | 'dark'
+}
+
+/**
+ * Creates a Preact component that dynamically imports and mounts a devtools panel. SSR friendly.
+ * @param devtoolsPackageName The name of the devtools package to be imported, e.g., '@tanstack/devtools-preact'
+ * @param importName The name of the export to be imported from the devtools package (e.g., 'default' or 'DevtoolsCore')
+ * @returns A Preact component that mounts the devtools
+ * @example
+ * ```tsx
+ * // if the export is default
+ * const [PreactDevtoolsPanel, NoOpPreactDevtoolsPanel] = createPreactPanel('@tanstack/devtools-preact')
+ * ```
+ *
+ * @example
+ * ```tsx
+ * // if the export is named differently
+ * const [PreactDevtoolsPanel, NoOpPreactDevtoolsPanel] = createPreactPanel('@tanstack/devtools-preact', 'DevtoolsCore')
+ * ```
+ */
+export function createPreactPanel<
+ TComponentProps extends DevtoolsPanelProps | undefined,
+ TCoreDevtoolsClass extends {
+ mount: (el: HTMLElement, theme: 'light' | 'dark') => void
+ unmount: () => void
+ },
+>(CoreClass: new () => TCoreDevtoolsClass) {
+ function Panel(props: TComponentProps) {
+ const devToolRef = useRef(null)
+ const devtools = useRef(null)
+ useEffect(() => {
+ if (devtools.current) return
+
+ devtools.current = new CoreClass()
+
+ if (devToolRef.current) {
+ devtools.current.mount(devToolRef.current, props?.theme ?? 'dark')
+ }
+
+ return () => {
+ if (devToolRef.current) {
+ devtools.current?.unmount()
+ }
+ }
+ }, [props?.theme])
+
+ return
+ }
+
+ function NoOpPanel(_props: TComponentProps) {
+ return <>>
+ }
+ return [Panel, NoOpPanel] as const
+}
+
diff --git a/packages/devtools-utils/src/preact/plugin.tsx b/packages/devtools-utils/src/preact/plugin.tsx
new file mode 100644
index 00000000..e472d71c
--- /dev/null
+++ b/packages/devtools-utils/src/preact/plugin.tsx
@@ -0,0 +1,31 @@
+/** @jsxImportSource preact */
+
+import type { JSX } from 'preact'
+import type { DevtoolsPanelProps } from './panel'
+
+export function createPreactPlugin({
+ Component,
+ ...config
+}: {
+ name: string
+ id?: string
+ defaultOpen?: boolean
+ Component: (props: DevtoolsPanelProps) => JSX.Element
+}) {
+ function Plugin() {
+ return {
+ ...config,
+ render: (_el: HTMLElement, theme: 'light' | 'dark') => (
+
+ ),
+ }
+ }
+ function NoOpPlugin() {
+ return {
+ ...config,
+ render: (_el: HTMLElement, _theme: 'light' | 'dark') => <>>,
+ }
+ }
+ return [Plugin, NoOpPlugin] as const
+}
+
diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json
index acf9bbd2..951ea176 100644
--- a/packages/devtools-utils/tsconfig.json
+++ b/packages/devtools-utils/tsconfig.json
@@ -9,6 +9,7 @@
"tsup.config.ts",
"eslint.config.js",
"vite.config.ts",
+ "vite.config.preact.ts",
"tests",
"vite.config.solid.ts"
]
diff --git a/packages/devtools-utils/vite.config.preact.ts b/packages/devtools-utils/vite.config.preact.ts
new file mode 100644
index 00000000..1132fc37
--- /dev/null
+++ b/packages/devtools-utils/vite.config.preact.ts
@@ -0,0 +1,26 @@
+import { defineConfig, mergeConfig } from 'vitest/config'
+import { tanstackViteConfig } from '@tanstack/vite-config'
+import packageJson from './package.json'
+
+const config = defineConfig({
+ plugins: [],
+ test: {
+ name: packageJson.name,
+ dir: './',
+ watch: false,
+ environment: 'jsdom',
+ setupFiles: ['./tests/test-setup.ts'],
+ globals: true,
+ },
+})
+
+export default mergeConfig(
+ config,
+ tanstackViteConfig({
+ entry: ['./src/preact/index.ts'],
+ srcDir: './src/preact',
+ outDir: './dist/preact',
+ cjs: false,
+ }),
+)
+
diff --git a/packages/preact-devtools/CHANGELOG.md b/packages/preact-devtools/CHANGELOG.md
new file mode 100644
index 00000000..5f9f50ef
--- /dev/null
+++ b/packages/preact-devtools/CHANGELOG.md
@@ -0,0 +1,8 @@
+# @tanstack/preact-devtools
+
+## 0.8.2
+
+### Patch Changes
+
+- Initial release of Preact devtools adapter
+
diff --git a/packages/preact-devtools/README.md b/packages/preact-devtools/README.md
new file mode 100644
index 00000000..ed3fdc19
--- /dev/null
+++ b/packages/preact-devtools/README.md
@@ -0,0 +1,58 @@
+# @tanstack/preact-devtools
+
+This package is still under active development and might have breaking changes in the future. Please use it with caution.
+
+## Usage
+
+```tsx
+import { TanStackDevtools } from '@tanstack/preact-devtools'
+import { PreactQueryDevtoolsPanel } from '@tanstack/preact-query-devtools'
+import { TanStackRouterDevtoolsPanel } from '@tanstack/preact-router-devtools'
+import { QueryClient, QueryClientProvider } from '@tanstack/preact-query'
+const queryClient = new QueryClient()
+function App() {
+ return (
+
+
My App
+ ,
+ },
+ {
+ name: 'TanStack Router',
+ render: ,
+ },
+ ]}
+ />
+
+ )
+}
+```
+
+## Creating plugins
+
+In order to create a plugin for TanStack Devtools, you can use the `plugins` prop of the `TanStackDevtools` component. Here's an example of how to create a simple plugin:
+
+```tsx
+import { TanStackDevtools } from '@tanstack/preact-devtools'
+
+function App() {
+ return (
+
+
My App
+ ,
+ },
+ ]}
+ />
+
+ )
+}
+```
+
diff --git a/packages/preact-devtools/eslint.config.js b/packages/preact-devtools/eslint.config.js
new file mode 100644
index 00000000..57ef959f
--- /dev/null
+++ b/packages/preact-devtools/eslint.config.js
@@ -0,0 +1,19 @@
+// @ts-check
+
+import pluginReactHooks from 'eslint-plugin-react-hooks'
+import rootConfig from '../../eslint.config.js'
+
+/** @type {import('eslint').Linter.Config[]} */
+export default [
+ ...rootConfig,
+ {
+ plugins: {
+ 'react-hooks': pluginReactHooks,
+ },
+ rules: {
+ 'react-hooks/exhaustive-deps': 'error',
+ 'react-hooks/rules-of-hooks': 'error',
+ },
+ },
+]
+
diff --git a/packages/preact-devtools/package.json b/packages/preact-devtools/package.json
new file mode 100644
index 00000000..41628442
--- /dev/null
+++ b/packages/preact-devtools/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@tanstack/preact-devtools",
+ "version": "0.8.2",
+ "description": "TanStack Devtools is a set of tools for building advanced devtools for your Preact application.",
+ "author": "Tanner Linsley",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/TanStack/devtools.git",
+ "directory": "packages/preact-devtools"
+ },
+ "homepage": "https://tanstack.com/devtools",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "keywords": [
+ "preact",
+ "devtools"
+ ],
+ "type": "module",
+ "types": "dist/esm/index.d.ts",
+ "module": "dist/esm/index.js",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ }
+ },
+ "./package.json": "./package.json"
+ },
+ "sideEffects": false,
+ "engines": {
+ "node": ">=18"
+ },
+ "files": [
+ "dist",
+ "src"
+ ],
+ "scripts": {
+ "clean": "premove ./build ./dist",
+ "test:eslint": "eslint ./src",
+ "test:lib": "vitest --passWithNoTests",
+ "test:lib:dev": "pnpm test:lib --watch",
+ "test:types": "tsc",
+ "test:build": "publint --strict",
+ "build": "vite build"
+ },
+ "dependencies": {
+ "@tanstack/devtools": "workspace:*"
+ },
+ "devDependencies": {
+ "@preact/preset-vite": "^2.10.2",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "preact": "^10.28.0"
+ },
+ "peerDependencies": {
+ "preact": ">=10.0.0"
+ }
+}
+
diff --git a/packages/preact-devtools/src/devtools.tsx b/packages/preact-devtools/src/devtools.tsx
new file mode 100644
index 00000000..6d3d8e78
--- /dev/null
+++ b/packages/preact-devtools/src/devtools.tsx
@@ -0,0 +1,308 @@
+import { useEffect, useMemo, useRef, useState } from 'preact/hooks'
+import { render } from 'preact'
+import { TanStackDevtoolsCore } from '@tanstack/devtools'
+import type { JSX } from 'preact'
+import type {
+ ClientEventBusConfig,
+ TanStackDevtoolsConfig,
+ TanStackDevtoolsPlugin,
+} from '@tanstack/devtools'
+
+type PluginRender =
+ | JSX.Element
+ | ((el: HTMLElement, theme: 'dark' | 'light') => JSX.Element)
+
+type TriggerProps = {
+ theme: 'dark' | 'light'
+}
+
+type TriggerRender =
+ | JSX.Element
+ | ((el: HTMLElement, props: TriggerProps) => JSX.Element)
+
+export type TanStackDevtoolsPreactPlugin = Omit<
+ TanStackDevtoolsPlugin,
+ 'render' | 'name'
+> & {
+ /**
+ * The render function can be a Preact element or a function that returns a Preact element.
+ * If it's a function, it will be called to render the plugin, otherwise it will be rendered directly.
+ *
+ * Example:
+ * ```jsx
+ * {
+ * render: () => ,
+ * }
+ * ```
+ * or
+ * ```jsx
+ * {
+ * render: ,
+ * }
+ * ```
+ */
+ render: PluginRender
+ /**
+ * Name to be displayed in the devtools UI.
+ * If a string, it will be used as the plugin name.
+ * If a function, it will be called with the mount element.
+ *
+ * Example:
+ * ```jsx
+ * {
+ * name: "Your Plugin",
+ * render: () => ,
+ * }
+ * ```
+ * or
+ * ```jsx
+ * {
+ * name: