A coding playground for practicing React components with live preview.
Coding workspace with live preview
- Dark mode (default) with light mode toggle
- Monaco editor with syntax highlighting
- Live preview with in-browser bundling
- File protection (editable vs read-only)
- Save progress to SQLite
- Reset to default button
- File tree navigation
- Browser back/forward support
- 5 practice problems included
npm install
npm run devVisit http://localhost:5173
Frontend: React, TypeScript, Vite, Monaco Editor, esbuild-wasm, React Router
Backend: Node.js, Express, TypeScript, SQLite
react-lld-playground/
├── problems/ # Problem definitions
│ ├── counter/
│ ├── todo-list/
│ ├── accordion/
│ ├── tabs/
│ └── fetch-users/
│ ├── problem.json
│ ├── editable.json
│ ├── preview-wrapper.jsx
│ └── setup/
│
├── server/ # Express backend
│ └── src/
│ ├── routes/
│ ├── services/
│ └── db/
│
└── client/ # React frontend
└── src/
├── pages/
├── components/
└── services/
- Select a problem from the list
- Edit code in Monaco editor (some files are read-only)
- Live preview updates as you type (500ms debounce)
- Save with Cmd/Ctrl+S or the Save button
- Reset to original code anytime
- In-browser bundling: esbuild-wasm bundles code in the browser, no server round-trips
- Virtual file system: Bundler reads from in-memory object instead of disk
- CSS handling: CSS imports converted to style tag injections
- Preview isolation: User code runs in sandboxed iframe
- Security: Backend validates all saves against editable.json, returns 403 if violated
- Path protection: All file reads restricted to setup/ directory
Create a directory in problems/ with this structure:
problems/
your-slug/
problem.json
editable.json
preview-wrapper.jsx
setup/
src/
App.jsx
components/
styles/
problem.json:
{
"slug": "your-slug",
"title": "Problem Title",
"version": 1,
"difficulty": "medium",
"description": "What to build.",
"tags": ["state", "events"]
}editable.json:
{
"files": [
"setup/src/App.jsx"
]
}Only files listed here can be edited by users.
preview-wrapper.jsx:
import App from "./setup/src/App.jsx";
export default function PreviewWrapper() {
return <div style={{ padding: 24 }}><App /></div>;
}GET /api/problems- List all problemsGET /api/problems/:slug/tree- File tree + editable listGET /api/problems/:slug/file?path=...- File contentGET /api/problems/:slug/preview-wrapper- Preview wrapperPOST /api/users/:userId/problems/:slug/snapshots- Save progressGET /api/users/:userId/problems/:slug/snapshots/latest- Get latest save
- User code runs in sandboxed iframe
- Backend validates all saves against editable.json
- Path traversal protection (prevents ../ attacks)
- solution/ directory never served
- 1MB request size limit
# Start both client and server
npm run dev- Login/Auth
- Compression middleware (zip the files when bulk-sending to client)
- ETag/Cache-Control headers
- Auto-save (every 30s)
- Snapshot history UI
- Problem search/filter
- Test runner (testcases to check whether the solution is correct or not)
- Multi-user auth
MIT
