Complete boilerplate for building cross-platform applications with RedwoodSDK — featuring a web application and companion Chrome extension with shared authentication and synchronized data.
It provides:
- 🌐 Web Application - Full-stack app with SSR, RSC, and edge deployment
- 🔧 Chrome Extension - Companion browser extension with React + TypeScript
- 🔄 Shared Authentication - Users stay logged in across both platforms
- 📱 Data Synchronization - Real-time sync between web and extension
Create a complete cross-platform system:
1. Clone this repository:
git clone https://github.com/redwoodjs/rwsdk-web-extension-starter
cd rwsdk-web-extension-starter
pnpm install
2. Start development:
# Start web application
pnpm run dev:web
# Start extension development (in another terminal)
pnpm run dev:extension
3. Load extension in Chrome:
- Open
chrome://extensions/
- Enable "Developer mode"
- Click "Load unpacked" and select the
extension/dist
folder
pnpm run dev:web
- Start web application dev serverpnpm run dev:extension
- Start extension build watcher
pnpm run build:web
- Build web application for productionpnpm run build:extension
- Build extension for Chrome Web Store
pnpm run deploy:web
- Deploy web app to Cloudflare Workerspnpm run package:extension
- Create extension.zip for Chrome Web Store
🔄 Synchronized Authentication - Users stay logged in across both platforms 🌐 Shared Backend - Both use the same RedwoodSDK API endpoints 📱 Cross-Platform - Seamless data sync between web app and browser extension ⚡ Real-time - WebSocket support across web and extension contexts
├── standard/ # RedwoodSDK web application
│ ├── src/ # App components and pages
│ ├── prisma/ # Database schema and migrations
│ └── wrangler.jsonc # Cloudflare deployment config
├── extension/ # Chrome extension companion
│ ├── src/ # Extension code (popup, background, content)
│ ├── manifest.json # Extension manifest
│ └── dist/ # Built extension (after npm run build)
└── README.md # This file
As long as you return a valid Response, RedwoodSDK is happy!
// worker.tsx
import { defineApp } from "rwsdk/worker";
import { route, render } from "rwsdk/router";
import MyReactPage from "@app/pages/MyReactPage";
export default defineApp([
render(Document, [
route("/", () => new Response("Hello, World!")),
route("/ping", function () {
return <h1>Pong!</h1>;
}),
route("/react", MyReactPage)
route("/docs", async () => {
return new Response(null, {
status: 301,
headers: {
"Location": "https://docs.rwsdk.com",
},
});
}),
route("/sitemap.xml", async () => {
return new Response(sitemap, {
status: 200,
headers: {
"Content-Type": "application/xml",
},
});
}),
route("/robots.txt", async () => {
const robotsTxt = `User-agent: *
Allow: /
Disallow: /search
Sitemap: https://rwsdk.com/sitemap.xml`;
return new Response(robotsTxt, {
status: 200,
headers: {
"Content-Type": "text/plain",
},
});
}),
]),
]);
RedwoodSDK is true Javascript full-stack:
// users.ts (server function)
"use server";
import { db } from "@/db";
export async function getUsers() {
const users = await db.users.findAll();
return users;
}
// UserList.tsx (React server component)
import { getUsers } from "./users";
export default async function UsersPage() {
const users = await getUsers();
return (
<div>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}