Enhanced interactive playground for tRPC with tabs, request management, and much more.
- π Intuitive and modern user interface
- π Tab system to organize your queries/mutations
- πΎ Export and import queries to share with your team
- π Default data loading for new users (Tabs, Headers, etc.)
- π§ HTTP headers customization (global & per-tab)
- 𧬠Variables support with type validation (global & per-tab)
- π± Environment variables injection (read-only, provided by the server)
- π Request history β replay, view, and diff (input & output) past calls side-by-side
- β¨ Smart autocomplete & inline linting based on your tRPC schema β deep support for nested objects, arrays, discriminated unions, and schema constraints (min/max, patterns, formatsβ¦)
- β¨οΈ Configurable keyboard shortcuts (run & search)
- π¨ Light & dark themes
- βοΈ Customizable settings (font size, timeout, split, history size, shortcuts)
- πͺ Built-in code formatter
- π’ Monorepo-friendly (isolate data per project)
- π Adapters for Fastify, Express, Koa and Next.js (App Router)
- π Support for more frameworks (Honoβ¦)
- π‘ Subscriptions support (WebSocket)
- ...and much more!
Feel free to suggest ideas or contribute on GitHub !
It's a dev tool, so install it as a dev dependency:
# npm
npm install --save-dev trpc-playground-plus
# yarn
yarn add --dev trpc-playground-plus
# pnpm
pnpm add --save-dev trpc-playground-plusWith Fastify
import { createFastifyAdapter } from 'trpc-playground-plus/adapters/fastify';
import { fastify } from 'fastify';
import { appRouter } from './router';
const app = fastify();
// Playground configuration
await createFastifyAdapter({
app,
trpcEndpoint: 'http://localhost:3000/api/trpc',
router: appRouter,
playgroundEndpoint: '/playground'
});
// Start server
await app.listen({ port: 3000 });
console.log('π Server available at http://localhost:3000');
console.log('π Playground available at http://localhost:3000/playground');With Express
import { createExpressAdapter } from 'trpc-playground-plus/adapters/express';
import express from 'express';
import { appRouter } from './router';
const app = express();
// Playground configuration
createExpressAdapter({
app,
trpcEndpoint: 'http://localhost:3000/api/trpc',
router: appRouter,
playgroundEndpoint: '/playground'
});
// Start server
app.listen(3000, () => {
console.log('π Server available at http://localhost:3000');
console.log('π Playground available at http://localhost:3000/playground');
});Works with Express 4 and 5. Static assets are served with the built-in express.static,
so there is nothing else to install.
With Koa
import { createKoaAdapter } from 'trpc-playground-plus/adapters/koa';
import Koa from 'koa';
import { appRouter } from './router';
const app = new Koa();
// Playground configuration
createKoaAdapter({
app,
trpcEndpoint: 'http://localhost:3000/api/trpc',
router: appRouter,
playgroundEndpoint: '/playground'
});
// Start server
app.listen(3000, () => {
console.log('π Server available at http://localhost:3000');
console.log('π Playground available at http://localhost:3000/playground');
});Works with Koa 2 and 3. The adapter is a single middleware that matches its own routes and
serves the playground assets itself, so neither @koa/router nor koa-static is required.
Koa runs middleware in registration order, so register the adapter before any catch-all middleware β otherwise the playground requests never reach it.
With Next.js (App Router)
// app/playground/[[...slug]]/route.ts
import { createNextHandler } from 'trpc-playground-plus/adapters/next';
import { appRouter } from '@/server/router';
export const { GET } = createNextHandler({
router: appRouter,
trpcEndpoint: '/api/trpc',
});That's it β no next.config change is needed: the playground assets are bundled inside the
adapter, so it works the same on Vercel, in standalone output and in Docker.
A few notes:
- The route must be an optional catch-all (
[[...slug]]) so the handler also receives/playground/app.jsand/playground/config. - There is no
appoption: the file's location is the mount point. Move the folder (or set abasePath) and the playground follows, with no code change. - Requires Next 13.4+ with the App Router. Next itself is never imported β the handler is a plain
Web
RequestβResponsefunction β so there is no peer dependency to install.
import { createFastifyAdapter } from 'trpc-playground-plus/adapters/fastify';
const myData = {
tabs: [
{
id: "tab-1",
title: "Get all users",
content: "trpc.user.getAll.query()",
isActive: true
},
],
headers: [
{
key: "Authorization",
value: "Bearer your-token-here",
enabled: true
}
],
};
await createFastifyAdapter({
app: fastify,
trpcEndpoint: '/api/trpc',
playgroundEndpoint: '/playground',
router: appRouter,
defaultData: myData // <- defaultData is optional but recommended for new user
});| Option | Type | Description | Default |
|---|---|---|---|
app |
FastifyInstance | Express | Koa |
Server instance the playground is mounted on | (required, except Next.js) |
trpcEndpoint |
string |
tRPC API Endpoint | (required) |
router |
Router |
tRPC Router | (required) |
playgroundEndpoint |
string |
Playground path (Next.js: derived from the route, override only if needed) | /playground |
transformer |
'superjson' |
Data transformer used by your tRPC client | undefined |
defaultData |
ExportData |
Default tabs/headers to bootstrap the playground | undefined |
envVariables |
Record<string, unknown> |
Read-only variables injected by the server, usable in queries by their key (like any variable) | undefined |
projectKey |
string |
Prefix for localStorage keys (monorepo isolation) | undefined |
If you use trpc-playground-plus in multiple projects served on the same domain (typical in a monorepo), the localStorage data would normally collide. Set a unique projectKey per project to isolate them:
// App A
await createAdapter({
app,
trpcEndpoint: '/api/trpc',
router: appRouter,
projectKey: 'app-a',
});
// App B (different project in same monorepo)
await createAdapter({
app,
trpcEndpoint: '/api/trpc',
router: appRouter,
projectKey: 'app-b',
});localStorage keys are then prefixed (e.g. app-a:trpc-playground-tabs), avoiding collisions. The projectKey is also embedded in exported JSON files so imports can warn when data is brought over from a different project.
Compatible with tRPC v11+ and zod 4.
Adapters are exposed as separate entry points, so only the framework you import is pulled in. The matching framework package is an optional peer dependency β install it in your own app:
| Adapter | Entry point | Peer dependencies |
|---|---|---|
| Fastify | trpc-playground-plus/adapters/fastify |
fastify ^5, @fastify/static ^8 |
| Express | trpc-playground-plus/adapters/express |
express ^4.18 || ^5 |
| Koa | trpc-playground-plus/adapters/koa |
none (Koa 2 or 3) |
| Next.js | trpc-playground-plus/adapters/next |
none (Next 13.4+, App Router) |
During a project, we encountered limitations with the trpc-playground solution, which is no longer maintained. It started as a proof of concept (POC) to address the specific needs we had β but it has since grown well beyond that into a real, actively maintained alternative for exploring and testing tRPC APIs, with a modern UI, smart autocomplete & linting, request history, environment variables, and an extensible multi-adapter architecture.
MIT Β© RΓ©my 'Raesta' Mulet