[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"news-article-pluggable-extensible-and-playful-devtools":3,"$f2v3tzw7dti6zg":-1},{"article":4,"engagement":22,"relatedArticles":24,"relatedVideo":7,"tags":25},{"content":5,"createdAt":6,"embedding":7,"id":8,"image":9,"isAffiliate":10,"isPublished":10,"publishedAt":11,"relatedVideoArticleId":7,"slug":12,"sourceId":13,"sourceName":14,"sourceSlug":15,"sourceType":16,"sourceUrl":17,"summary":18,"title":19,"updatedAt":20,"url":21},"[[toc]] Over the years, I have built quite a few DevTools: {UnoCSS Inspector}, {Vite Plugin Inspect}, {Vitest UI}, {Nuxt DevTools}, {ESLint Config Inspector}, and {Node Modules Inspector}, among others. They look quite different, but fundamentally they all try to do the same thing: make implicit state visible. Instead of guessing why a CSS utility was generated, how a module was transformed, or which configuration applies to a file, we can see the process directly and interact with it. Despite their different purposes, these tools share a surprising amount of infrastructure: client-server communication, state synchronization, serialization, static asset hosting, and a web interface. Each one also needs to decide how it is packaged, distributed, mounted to a server, and connected to its host. In practice, every tool ends up rebuilding many of the same pieces in isolation. The same pattern appears across the ecosystem. Frameworks and build tools are building their own DevTools, often with overlapping capabilities such as data inspectors, asset viewers, build analyzers, terminals, and editor integrations. Yet most of them are tied to a specific framework and to the details of its development server: how it serves assets, handles requests, and upgrades connections. As a result, similar features are rebuilt and improved separately. What if we could free DevTools from those boundaries? If each capability were reusable and modular, it could benefit every supported host. Instead of spreading the work across several versions of the same idea, communities could join forces on one tool and make it much better together. This is the vision of a Universal DevTools Ecosystem I started sharing back in 2023, in Now, and the Future of Nuxt DevTools and Anthony's Roads to Open Source - The Set Theory: The diagram was aspirational. The direction felt right, but finding the boundary that could make it work was much harder. The idea stayed with me as the work moved from Nuxt DevTools to Vite DevTools. When I joined Vercel and started working on Vite DevTools, I finally had the opportunity to explore it on a broader scale. Vite gave us a concrete home to prove the experience, but the goal was always to open it to other build toolchains. Each iteration taught us something new, while LLMs made it much faster to explore and validate the design. Gradually, the right boundary started to emerge. Today, the vision is finally becoming something not far away. Let me introduce you to Devframe. Devframe Devframe is a framework-neutral foundation for defining a DevTool once, then bringing it to different hosts, standalone surfaces, and agents. You can think of Devframe as a framework for building DevTools, in the same way Nuxt or Next.js provides a framework for building web applications. At the integration layer, it plays a role similar to unplugin: while unplugin gives plugins a common interface across bundlers, Devframe gives DevTools a common definition across hosts. A Devframe definition describes one tool: its capabilities, RPC functions, shared state, web interface, diagnostics, and agent-facing surface. From that definition, Devframe creates a Web Standard request handler that can be mounted almost anywhere. One Definition, One Standard Handler, Many Adapters Every Devframe starts with defineDevframe(). At its core, it associates a tool's identity with the capabilities it provides: \u002F\u002F my-tool.ts import { defineDevframe } from 'devframe' import { inspectProject } from '.\u002Frpc' export default defineDevframe({ id: 'my-tool', name: 'My Tool', \u002F\u002F Package metadata and client entry omitted... setup(ctx) { ctx.scope('my-tool').rpc.register(inspectProject) }, }) The definition is independent of its presentation. initDevframe() turns it into a live instance: \u002F\u002F server.ts import { initDevframe } from 'devframe\u002Finitiate' import myDevframe from '.\u002Fmy-tool' const myTool = initDevframe(myDevframe, { base: '\u002F__my-tool\u002F', }) myTool.handler \u002F\u002F Web Standard Request -&gt; Response handler \u002F\u002F (request: Request) =&gt; Promise&lt;Response) myTool.nodeMiddleware \u002F\u002F Traditional connect-style middleware \u002F\u002F (req: IncomingMessage, res: ServerResponse, next: () =&gt; void) =&gt; void The handler becomes the tool's boundary. Behind it, Devframe serves the web interface, connection metadata, live RPC, authentication, and optional MCP endpoint under one namespace. The tool is no longer tied to a particular development server API; all the host needs to understand is the Web Standard Request and Response. This handler-first model is greatly inspired by Comark Content. Modern frameworks, runtimes, and build tools already converge around this boundary. Hono and Nitro work with Web Standard requests directly. Next.js and SvelteKit expose route handlers. Vite and Rsbuild accept Connect-style middleware, for which the same instance provides nodeMiddleware: That is almost the entire portability trick. Any framework or build tool that supports Web Standard handlers or Connect-style middleware can mount the same Devframe and gain access to the same ecosystem. Adapters as Conveniences The handler is the smallest common denominator. For common entry points, higher-level adapters package it into familiar forms. The same definition can become a standalone CLI, a dedicated dev server, a Vite DevTools plugin, an MCP server, or a static report: import { createPluginFromDevframe } from '@vitejs\u002Fdevtools-kit\u002Fnode' import { createBuild } from 'devframe\u002Fadapters\u002Fbuild' import { createCac } from 'devframe\u002Fadapters\u002Fcac' import { createDevServer } from 'devframe\u002Fadapters\u002Fdev' import { createMcpServer } from 'devframe\u002Fadapters\u002Fmcp' import myDevframe from '.\u002Fmy-tool' \u002F\u002F Pick the entry points your package needs: export const runCli = () =&gt; createCac(myDevframe).parse() export const startServer = () =&gt; createDevServer(myDevframe) export const vitePlugin = createPluginFromDevframe(myDevframe) export const startMcp = () =&gt; createMcpServer(myDevframe, { transport: 'stdio' }) export const buildReport = () =&gt; createBuild(myDevframe, { outDir: 'dist-static' }) A package can ship several of these entry points at once. For example, a build inspector could offer a standalone CLI for any project, generate static reports in CI, appear as a dock inside Vite DevTools, and let an agent query the active build—all backed by the same definition. We are already using this model in {Node Modules Inspector}, {ESLint Config Inspector}, and {Vite Plugin Inspect}. They remain focused tools with their own interfaces, while sharing Devframe underneath. You can find more examples on the Built with Devframe page. The frontend is up to each tool as well. Devframe handles the protocol and runtime, while the tool can choose whichever UI framework and design system suits it. To dogfood that promise, the built-in plugins span Vue, Svelte, Solid, React, and Next.js. Visual and Agentic As agents become part of our development workflows, a DevTool no longer has to be only a panel for humans. Our goal is for it to also offer a structured interface to its internal state and capabilities—something agents and other tools can access programmatically. The two interfaces play to different strengths rather than replacing each other. Visualizations are effective for exploration, overview, and comparison. Agents can retrieve focused context, correlate it with the codebase, and carry out multi-step actions. The presentation changes, but the source of truth stays the same. In Devframe, RPC functions stay private by default and must be explicitly exposed to agents. The MCP adapter translates those functions, readable resources, and selected shared state into an agent-consumable surface. Descriptions, schemas, and safety metadata help agents understand when and how each capability should be used. There is another interesting piece here. Devframe integrates with Vercel's json-render, allowing a UI to be described as serializable data from a constrained component catalog. This makes it easier for agents to generate dashboards and interactive tools while keeping the output predictable. The same mechanism also enables server-provided UI: a Devframe publishes the view and its state, while the host provides the renderer. With the prebuilt reference UI, a tool can get started without authoring or building a custom client at all. The protocol remains renderer-agnostic, so each host can render the same view with its own framework, components, and design system. We are still exploring the APIs and practices around discoverability, permissions, context usage, and the relationship between visual and agentic workflows. We would love to hear ideas and advice from the community as these patterns evolve through real integrations. Built-in Plugins Of course, an abstraction only becomes convincing when real tools can live on it. To test Devframe's capabilities and framework-agnostic design, we ship a few official plugins as reusable working examples. They are intentionally built with different frontend frameworks, and each can run standalone or be mounted into a supported host. Here are a few examples: Data Inspector @devframes\u002Fplugin-data-inspector is built with Vue and provides an interactive workbench for live server-side objects. A tool can register an object as a data source, then explore and query it with Jora inside the process that owns it. Standalone, it can inspect JSON or JSONL files, build a self-contained report, or attach to a running Node.js process. This is useful for inspecting stores, caches, framework contexts, build metadata, or other states that would otherwise require custom logging. You can try it standalone with: pnpx @devframes\u002Fplugin-data-inspector Data Inspector exploring a live data source When integrated, other tools only need to contribute data sources. A Vite plugin could expose its plugin container, a framework could expose runtime state, and a test runner could expose its test graph. All of them can reuse the same query workbench and data viewer instead of building another inspector for every host. Terminals @devframes\u002Fplugin-terminals is built with Svelte and provides a browser-based terminal panel supporting read-only process output and interactive PTY sessions. This separates the process-running capability from the tool that renders it. A host can give multiple tools a consistent place for subprocess output and interactive commands, without mixing every task into the user's main terminal. Terminals plugin running in Vite DevTools It can also run standalone: pnpx @devframes\u002Fplugin-terminals Terminals plugin running as a standalone page This opens the interactive terminal directly in your browser. You can use it to manage processes, run commands, or even run agents like Claude Code without leaving the browser. Accessibility Inspector @devframes\u002Fplugin-a11y is built with Solid. It scans the host application with axe-core, lists WCAG violations, and highlights the corresponding elements on the page. It can also turn the findings into fix prompts for agents, connecting visual inspection with an agentic workflow. Standalone, its panel and injected scanner can inspect any page. Inside a DevTools host, the same findings can also be mirrored into the shared message feed. It is heavily inspired by @nuxt\u002Fa11y, which brought real-time accessibility feedback into Nuxt DevTools. Extracting the idea into a Devframe plugin makes the same capability available beyond Nuxt. Accessibility Inspector highlighting violations in the host application More Plugins Other official plugins cover a VS Code editor on the web, asset management, a Git panel, Open Graph previews, and Devframe's own RPC and state inspector. What they share is the Devframe definition and protocol, not a frontend stack. These plugins are not meant to be a complete set of tools. They show what Devframe can support and offer starting points for communities to build their own. I believe many more interesting DevTools will emerge over time. You can follow the growing list on Built with Devframe. From One Devframe to a DevTools Host So far, we have one portable DevTool. But once several Devframes are active together, another problem appears: discovery. How do users find and move between them? Many DevTools log their own URL to the console: \u001b[2m~\u001b[0m \u001b[34mpnpm dev\u001b[0m \u001b[1;36mVITE\u001b[0m \u001b[2mv8.2.1\u001b[0m \u001b[32mready in\u001b[0m \u001b[2m32 ms\u001b[0m \u001b[32m➜\u001b[0m \u001b[1mLocal:\u001b[0m \u001b[1;4;36mhttp:\u002F\u002Flocalhost:3333\u002F\u001b[0m \u001b[1;35mUnoCSS Inspector:\u001b[0m \u001b[3;32mhttp:\u002F\u002Flocalhost:3333\u002F__unocss\u002F\u001b[0m \u001b[2m&gt;\u001b[0m \u001b[33mVisualized ESLint Config:\u001b[0m \u001b[4;34mhttp:\u002F\u002F127.0.0.1:3333\u002F.eslint-config\u002F\u001b[0m \u001b[32m➜\u001b[0m \u001b[1mVite Inspect:\u001b[0m \u001b[1;3;36mhttp:\u002F\u002Flocalhost:3333\u002F__inspect\u002F\u001b[0m Sometimes DevTools also inject floating buttons into the host application: Floating buttons from multiple DevTools injected into the host application.(this is a made-up example to demonstrate the problem) As more tools join the project, the console becomes a directory of URLs and the page gains a collection of unrelated floating buttons. While that each DevTool also has to build and maintain its own discovery mechanism. To improve this, Devframe also provides a composition layer: the Hub. @devframes\u002Fhub is headless and framework-neutral. Multiple Devframes can register with it and contribute docks, commands, messages, terminals, and shared state. To users, they appear through one consistent entry point. To the tools, the Hub provides a shared context in which they can discover and collaborate with one another. The same mounting model scales from one Devframe to the whole collection. initHub() puts the Hub and all of its Devframes behind one Web Standard handler: import { initHub } from '@devframes\u002Fhub\u002Finitiate' import { createTerminalsDevframe } from '@devframes\u002Fplugin-terminals' import { createXxxDevframe } from '...' const hub = initHub({ \u002F\u002F The common base path for all mounted Devframes. \u002F\u002F `\u002F__my-tool\u002F` becomes `\u002F__devframes\u002F__my-tool\u002F`. base: '\u002F__devframes\u002F', \u002F\u002F Devframes become composable plugins of the Hub. devframes: [ createTerminalsDevframe(), createXxxDevframe(), \u002F\u002F ... ], \u002F\u002F We ship a reference UI to make it easy to get started, \u002F\u002F but you can provide your own layer to match \u002F\u002F your product's design system and interaction model. ui: await import('@devframes\u002Fhub-ui').then(m =&gt; m.createUi()), }) \u002F\u002F The same handler\u002Fmiddleware API as a standalone Devframe. hub.handler hub.nodeMiddleware With the Hub, DevTools can register themselves under one consistent entry.(this is a made-up example for demonstration) Mounted Devframes share one RPC registry, state store, connection, authentication gate, and optional aggregate MCP endpoint. The Hub itself remains headless: @devframes\u002Fhub-ui provides the reference interface, while a product can bring its own UI without changing the underlying tools. Like a single Devframe, with the standard handler, the Hub can also be mounted to almost any framework. The repository includes working reference hosts for Vite, Next.js, Hono, Nitro, and Rsbuild. Each host only connects the same handler and UI entry to its native server API. While the examples are minimal to demonstrate the possibilities, a more complete host that matches the product's design system and interaction model can also be shipped on top of the Hub's foundation. Vite DevTools Vite DevTools is the first flagship host built on this foundation. It brings a Vite-focused interface and its own integrations while using initHub() for composition and serving. Alongside Vite and Rolldown analysis, Vitest UI, and Oxc tooling, it gives independent DevTools a common place to work together. Vite Plus dock entry in Vite DevTools Rolldown DevTools in Vite DevTools A Devframe can join Vite DevTools through an adapter. A regular Vite plugin can also contribute directly through the new devtools.setup entry: \u002F\u002F vite.config.ts import { createPluginFromDevframe } from '@vitejs\u002Fdevtools-kit\u002Fnode' import { createMyDevframe } from 'my-devframe-tool' import { defineConfig } from 'vite' const myDevframe = createMyDevframe() export default defineConfig({ devtools: true, plugins: [ \u002F\u002F Helper to turn a Devframe into a Vite plugin. createPluginFromDevframe(myDevframe), \u002F\u002F A regular Vite plugin can also contribute directly. { name: 'vite-plugin-my-tool', devtools: { setup(ctx) { \u002F\u002F Devframe context with Vite-specific augmentations. }, }, }, ], }) The adapter turns an existing Devframe into a Vite plugin. The devtools.setup entry lets Vite plugins use the same context without creating another integration layer. This makes adoption incremental: tools can start where they are and still participate in the shared ecosystem. Nuxt DevTools The new Nuxt DevTools v4 builds on top of both. It inherits Vite DevTools and Vue DevTools, then adds Nuxt-specific knowledge: pages, modules, auto-imports, server APIs, runtime state, and contributions from the Nuxt module ecosystem. Nuxt DevTools v4 Here are the stacking layers to demonstrate this better: Vue DevTools is migrating to the Vite DevTools foundation. Vue capabilities such as component and reactivity inspection can then coexist with Vite integrations and general Devframes in the same host. In a way, the story has come full circle: the wish that started with Nuxt DevTools now returns with a concrete foundation underneath. Nuxt DevTools v4 is expected to ship with Nuxt v5 and will also be available as a manual opt-in for Nuxt 4. Next.js DevTools (Prototype) I am also experimenting with bringing Devframes to Next.js DevTools. Internally, I already have a working prototype of the Devframes Hub running inside Next.js DevTools, without any modifications to the installed Devframe plugins. Next.js DevTools with Devframes prototype(this is an internal prototype, it's not yet available and does not represent the final state) Inheriting the Ecosystem Sharing the foundation does not mean every DevTools experience should look the same. Framework-specific layers can be much richer because they understand their framework's conventions and runtime. The infrastructure can be shared while the final experience remains specific. Devframe itself remains independent of Vite and any framework. A future framework-specific DevTools host can mount the same Hub and plugins, then add its own knowledge and presentation. It will not get the full experience for free, but it no longer needs to rebuild the foundation before it can begin. Build Your Own DevTools Devframe is not only for framework authors or established tooling teams. It can also provide the skeleton for project-specific DevTools and even one-off visualizations. With built-in agent skills and a growing collection of real-world examples, we are exploring a future where you might ask an agent: &quot;Build me a one-off Devframe to visualize my app's network request flow and highlight the bottlenecks.&quot; Not every useful DevTool needs to become a permanent product or a published package. Some might exist only long enough to answer one question. We will keep improving Devframe and its ecosystem so these pluggable, extensible, and playful tools become practical for more people to build. What's Next Devframe v1.0 stabilizes the interface for the community to build on and experiment with. Vite DevTools will follow with a stable release, while Nuxt DevTools v4 and the Vue DevTools migration continue testing the model at the framework level. This is the first credible implementation toward the modular DevTools infrastructure we imagined years ago. There are more frameworks to connect, plugin conventions to refine, and agentic practices to discover. What excites me is not one particular feature or integration, but the possibility that a good tool can be built once, travel further, and become better as more communities contribute to it: shared infrastructure underneath, specific and playful experiences on top, and structured capabilities available to both humans and agents. We are still exploring the best practices, especially around agentic interfaces, permissions, and cross-tool collaboration. Any kind of contribution is welcome: integrations, experiments, design ideas, use cases, feedback, or simply trying the tools and sharing what you find. If this direction sounds interesting to you, check out the {Devframe} repository, try building something with it, leave us some feedback, or join the Discord. I am looking forward to seeing what we can build together! Thanks This vision has come a long way with the help of many people. A huge thank you to {@webfansplz}, who has put a tremendous amount of work into Vite DevTools. I also owe a lot to {@Akryum}: his work on Vue DevTools and testing framework UIs has inspired me for years, and he spent a great deal of time brainstorming and prototyping these DevTools ideas with me. {@hyfdev} helped coordinate with Rolldown and shape the APIs that made Vite DevTools possible. {@Atinux} planted the seed of Nuxt DevTools, invested so much in building it, and now continues that investment in Vite DevTools. {@danielroe} provided valuable feedback on Nuxt DevTools and kept motivating us to push further on bundle size (the installed size of Vite DevTools core dropped by 30 MB from v0.1 to v0.5). {@posva} provided great feedback on Devframe's documentation and helped with the integrations. Thanks also to {@yuyinws} for donating Oxc Inspector to Vite Plus DevTools and continuing to maintain it; and to {@SaKaNa-Y} and {@dvcolomban} for being early adopters and contributing extensively to both Vite DevTools and Devframe. And, of course, thanks to everyone who has contributed to Vite DevTools, Nuxt DevTools, and Vue DevTools along the way. This work is built on top of all those contributions. Finally, thanks to {Vercel} for supporting these projects and making our ambitious plan for unified DevTools no longer feel like an unreachable dream.","2026-09-16T08:00:11.451Z",null,"01a0a93b-00ba-7019-9e06-9d29d5e473ea","https:\u002F\u002Fantfu.me\u002Fimages\u002Fdevframe\u002Fdevframe-with-title.svg",false,"2026-09-16T00:00:00.000Z","pluggable-extensible-and-playful-devtools","ba9683a9-e13c-478d-92b6-be507359f672","Anthony Fu","anthony-fu","rss","https:\u002F\u002Fantfu.me","","Pluggable, Extensible, and Playful DevTools","2026-09-16T08:00:30.666Z","https:\u002F\u002Fantfu.me\u002Fposts\u002Fpluggable-extensible-playful-devtools",{"clickCount":23,"viewCount":23},0,[],[]]