GuidesPacks
Create a Browser Extension
Create a WXT and React browser extension next to a web app and a Hono tRPC API on Bun with Drizzle and SQLite in one Better Fullstack workspace.
Use the Browser Extension pack when an extension and a web app should share one backend: the extension lives in its own workspace app, and the API and database sit beside it.
Generate the extension starter
npm create better-fullstack@latest my-extension -- \ --ecosystem typescript \ --frontend tanstack-router \ --backend hono \ --runtime bun \ --database sqlite \ --orm drizzle \ --auth none \ --api trpc \ --addons wxt turborepo \ --css-framework tailwind \ --ui-library shadcn-uiWhat the pack includes
- A WXT extension in
apps/extensionbuilt from WXT's React starter shape, with a popup, a background script, and a content script. - A TanStack Router web app in
apps/webwith a tRPC client. - A Hono server on Bun in
apps/serverthat mounts tRPC at/trpcand returnsOKfromGET /. - A tRPC router with
healthCheckand publictasksprocedures for listing, creating, completing, and removing tasks. - Drizzle on SQLite through libSQL, with
DATABASE_URLpointing atlocal.dbin the project root. - A Turborepo workspace, so one command starts the server, the web app, and the extension.
Generated shape
my-extension/
apps/
extension/
wxt.config.ts
entrypoints/
background.ts
content.ts
popup/
App.tsx
main.tsx
index.html
server/
.env
src/index.ts
web/
packages/
api/src/routers/index.ts
db/src/
schema/task.ts
tasks.tsThe content script matches *://*.google.com/* and logs a message, and the background script logs the extension ID. Both are placeholders to replace.
Run it locally
npm installnpm run db:pushnpm run devnpm run dev runs turbo dev, which starts the server on port 3000, the web app on port 5173, and wxt --port 5555 for the extension in Chrome development mode. Installing dependencies also runs wxt prepare in the extension app.
The command lists turborepo next to wxt on purpose. Passing --addons replaces the default addon set, and without Turborepo the npm root dev script runs npm run dev --workspaces, which starts workspaces one after another.
From apps/extension, npm run dev:firefox targets Firefox, npm run build writes a production build, and npm run zip packages it for a store upload.
Call the API from the extension
The extension does not call the API yet. Grant host permission for the API origin in apps/extension/wxt.config.ts:
import { defineConfig } from "wxt";
export default defineConfig({
modules: ["@wxt-dev/module-react"],
manifest: {
host_permissions: ["http://localhost:3000/*"],
},
});Then fetch from the popup or the background script:
const response = await fetch("http://localhost:3000/");
const status = await response.text();For typed calls, add @trpc/client and the @my-extension/api workspace package to the extension's dependencies and create a client with httpBatchLink pointed at http://localhost:3000/trpc, the same way apps/web/src/utils/trpc.ts does.
First things to customize
- Replace the content script's
matchespattern with the sites the extension should run on. - Add auth before the
tasksprocedures hold user data. They are public procedures in this pack because the command uses--auth none. - Move the API URL into WXT config or an environment value per build, and list the production API origin in
host_permissions. - Keep secrets on the server. Anything bundled into the extension can be read by its users.
When this is the right pack
Choose this pack when the extension is one client of a product that also has a web app and an API. If the extension needs no backend, run the same command with --backend none --runtime none --database none --orm none --api none and keep only the extension and web apps. For the API side, read Hono with tRPC and Drizzle.