About tech-stack
A Claude Code skill documenting FixMyBerlin/FMC’s default stack and conventions for geo-focused React single-page apps. It guides app creation and cross-cutting changes, with references to specialized skills and setup templates.
Key features
- 01React 19 and TypeScript defaults
- 02Bun and Vite build conventions
- 03Geo and map library choices
- 04Lint, formatting, and browser targets
- 05Data, state, and styling choices
- 06Scaffold and CI templates
Use cases
- 01Choosing a stack for a new geo-focused React app
- 02Reviewing dependencies and app-wide conventions
- 03Setting up scripts, TypeScript, and tooling
- 04Finding specialized guidance for maps or testing
MCP support
Includes Claude Code MCP setup guidance for Postgres schema inspection, database queries, and browser exploration; references setup templates and related documentation.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ fixmyberlin/ fixmyskills --skill tech-stackAfter installing, type/tech-stackin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
examples/bunfig.netlify.toml.templatebunfig.toml.templateci.yml.templatedependabot.yml.templateknip.config.mjs.templatemcp.json.templateoxfmt.config.mjsoxlint.config.mjspackage-json-scripts.md.templatetsconfig.app.jsontsconfig.scripts.jsonvscode.extensions.json.templatevscode.settings.typescript.json.templateexamples/husky/ensure-bun.sh.templatepre-push.templatereferences/agent-browser-mcp.mdbrowser-target.mdbun-install-verify.mdbun-install.mddependabot.mdknip.mdllm-resources.mdoxc-config.mdpackage-json-scripts.mdpostgres-mcp.mdDetails
From the project READMEFMC tech stack
Default stack for geo-heavy React SPAs at FixMyBerlin / FMC. Use for tech decisions when creating or changing an app — not as a deep dive into any one library.
Install sibling skills separately: bunx skills add FixMyBerlin/fixmyskills --skill <name> -g -a claude-code -y
When to apply
- Greenfield app scaffold (dependencies, tooling, folder layout)
- Adding or renaming
package.jsonscripts - Library or pattern choice (“should we use X?”)
- Cross-cutting convention review on an existing app
- Claude Code MCP setup for Postgres schema inspection, agent DB queries, or browser exploration MCP
LLM resources
Load references/llm-resources.md only for the area the task touches.
Maps: load skill react-map-gl (and upstream React Map GL docs when the skill points there).
Specialized skills
Refer to skills by name; install missing ones with the command above.
| Area | Skill |
|---|---|
| Rust/WASM geo | rust-wasm-geo |
| Maps (react-map-gl) | react-map-gl |
| React TS patterns, useEffect discipline | react-dev |
| TanStack Router (search, loaders, Query) | tanstack-router-conventions (refs: params-search-ui-routes.md, router-search-serialization.md for pretty URLs, router-and-query.md, devtools.md) |
| TanStack Start (boundaries, SSR, server) | tanstack-start-conventions (ref: app-structure.md for folder layout) |
| URL state (with TanStack Router) | tanstack-router-conventions → params-search-ui-routes.md |
| URL state (Vite app without Router) | nuqs |
| Client global state | zustand-state-management |
| Prisma schema naming, migrations | prisma |
| E2E / Playwright | playwright-skill |
Runtime and build
- Runtime / package manager: Bun — install policy bun-install.md (≥ 1.3.14, global store,
bun --bunfor Vite,.nvmrcfor Prisma/Playwright; Docker image install and Netlify function packaging) - Build: latest Vite (8+)
- Lint / format: oxlint and oxfmt with fix flags; Prettier-compatible defaults:
- class sorting, import sorting,
package.jsonsorting printWidth100, semicolonsasNeeded, single quotes'typescript/switch-exhaustiveness-check': 'error'- React Compiler: oxlint
reactplugin +'react/unsupported-syntax': 'error'; Vite viaviteReact({ compiler: true })+oxc-transform-react(not Babel) - Templates: examples/oxfmt.config.mjs, examples/oxlint.config.mjs
- Setup and per-project tuning: references/oxc-config.md
- class sorting, import sorting,
- Client browser target:
browserslistinpackage.jsondrives Vite clientbuild.targetandeslint-plugin-compatin oxlint — references/browser-target.md
React and TypeScript
- UI: React 19
- TypeScript (default):
typescript@^7(native port; 7.0 release). Typecheck withtsc --noEmitviabun run type-check. The workspacetypescriptpackage provides both CLI (tsc) and editor language service — do not add a separate compiler binary for type-check. Nightlies:typescript@next(not the retired@typescript/native-previewpackage). - Editor/CLI alignment:
js/ts.tsdk.pathmust resolve to the samedevDependencies.typescriptinstall astsc(package root, notlib/). Misalignment makes IDE diagnostics disagree withbun run type-check. - Scaffold (TS 7):
package.json—devDependencies.typescript^7, script"type-check": "tsc --noEmit"(add-pwhen using split tsconfigs); pin@typescript/typescript-darwin-arm64/@typescript/typescript-linux-x64inoptionalDependenciesat the same version when CI runs on a different platform than dev (thetypescriptpackage lists these as optional — extend for other platforms as needed).bunfig.toml— bun-install.md. Commit.vscode/settings.json(template: examples/vscode.settings.typescript.json.template) and recommend the TypeScript 7 extension in.vscode/extensions.json(examples/vscode.extensions.json.template). - Monorepos: when TypeScript lives in a package subfolder, prefix
js/ts.tsdk.pathto that package’snode_modules/typescript(e.g.app/node_modules/typescript— tilda-geo). - Cursor / VS Code: install TypeScript 7 (
TypeScriptTeam.native-preview— marketplace ID unchanged). Enable via command palette TypeScript: Enable TypeScript 7 or the settings template. Copy examples/vscode.settings.typescript.json.template into.vscode/settings.json(merge with oxc keys from references/oxc-config.md). Toggle back with Disable TypeScript 7 Language Server when editor plugins still require TS 6 (Vue, Astro, Svelte, Angular templates — see release notes). - Claude Code MCP (user-level):
claude mcp add --scope useronly (~/.claude.json) — examples/mcp.json.template. Postgres: references/postgres-mcp.md. Browser exploration: references/agent-browser-mcp.md. - Claude Code rules (app-level): copy examples/package-json-scripts.md.template →
.claude/rules/package-json-scripts.mdon scaffold — references/package-json-scripts.md
Verify: node -e "console.log(require('typescript/package.json').version)", tsc --version, and bun run type-check — all 7.x. In the editor, confirm TypeScript 7 is enabled (status bar / command palette). Do not add typescript to repos that have no TS source (e.g. this skills monorepo).
- React Compiler: on by default — see skill
react-devfor memoization and typing conventions - GeoJSON:
@types/geojsonfor all GeoJSON payloads - Dates / times:
@date-fns/tz - Validation: Zod 4
tsconfig templates
Use two profiles — do not merge app and scripts into one config.
| Profile | Template | Profile analogue |
|---|---|---|
| App (DOM, TanStack Start) | examples/tsconfig.app.json | total-typescript bundler/dom |
| Scripts (CLI, no DOM) | examples/tsconfig.scripts.json | total-typescript bundler/no-dom |
Required type packages whenever compilerOptions.types includes bun-types / web — these names are npm packages, not built-in. Install as direct devDependencies (Knip and globalStore will not treat a transitive dep as yours — knip.md / bun-install.md):
@types/bunpinned to the Bun floor (1.3.14)bun-typesas a direct dep (^that floor) —typeslooks up the package namebun-types;@types/bunalone is not enough@types/webif the config lists"web"
Scripts profile (types: ["bun-types"]) still needs both @types/bun and bun-types even with no DOM. Bun scripts should use Glob / Bun.file(); that is only type-safe after these deps — otherwise agents fall back to node:fs.
Copy and adapt on scaffold. Adjust paths to project layout (./src/* vs ./*). Add allowJs: true only when the repo still has .js files.
TanStack Start (app only): verbatimModuleSyntax: false — type-only imports can become empty runtime imports and leak server code into client bundles. See header comments in tsconfig.app.json.
Never install or extends @total-typescript/tsconfig — keep settings inlined with deviation comments.
Typecheck:
tsc --noEmit -p tsconfig.app.json && tsc --noEmit -p tsconfig.scripts.json
Single-config repos (e.g. one root tsconfig.json covering app + scripts) are acceptable; greenfield TanStack Start apps should prefer split configs.
Astro / single-config: keep extends: astro/tsconfigs/strict. Set types to ["bun-types", "web"]. Do not add vite/client (Astro client types come from .astro/types.d.ts). Scripts in the same tsconfig still need Bun types (same packages as above).
Optional root tsconfig.json with "references" to both child configs.
ES2025: target and lib stay in sync. New ES2025 runtime APIs: use deliberately on the client, fine on server/Bun/scripts. Which browsers get that client bundle: references/browser-target.md.
Component typing, Compiler, oxlint React rules, and useEffect discipline: skill react-dev. Map camera, clicks, and layers: skill react-map-gl (not useEffect + map.on()).
Data and state
| Need | Choice |
|---|---|
| Server / async data | TanStack Query — no raw useEffect fetch; Suspense where the router supports it |
| Forms (non-trivial) | TanStack Form |
| Routing | TanStack Router / Start |
| Shareable URL state (with TanStack Router) | Route validateSearch (Zod), never nuqs — see tanstack-router-conventions → params-search-ui-routes.md |
| Shareable URL state (Vite app without TanStack Router) | nuqs — see skill nuqs |
| High-frequency URL updates | @tanstack/react-pacer — see https://tanstack.com/pacer/latest |
| Global client state | Zustand (tkdodo patterns) — skill zustand-state-management |
| Local UI state | useState |
| Prisma schema / migrations | skill prisma — naming, relation fields, migrate workflows |
Loader + Query integration: skill tanstack-router-conventions → router-and-query.md.
Styling and UI
- CSS: Tailwind CSS,
@tailwindcss/forms,@tailwindcss/typography - Class merging:
tailwind-mergewithtwJoin,twMerge - Fonts: Fontsource imports
- Accessibility: semantic HTML; keyboard-accessible map controls
Maps and geo (JS)
Stack choices only — map component patterns, layers, events, URL viewport: skill react-map-gl.
| Choice | Default |
|---|---|
| Library | react-map-gl/maplibre (not raw MapLibre in components) |
| Basemap | OpenFreeMap; Maptiler if required |
| Coordinates | WGS84 at API boundaries; validate with Zod |
| Turf | @turf/* per-function imports — not import * as turf |
| Feature editing | @osm-editor-kit/react-map-gl-draw (controlled value / onChange, styles as layer styles); example: tilda-geo drawing. Used before, still a possible alternative: TerraDraw with maplibre-gl-terradraw |
Rust / WASM geo
Turf vs WASM, crates, Vite wiring: skill rust-wasm-geo.
Dependency updates (Dependabot)
- Schedule by project type — AskUserQuestion if unclear. FixMyCity: weekly Monday. Private OSS and Astro: first Friday of the month. Same template. Details: dependabot.md.
- One open PR at a time per ecosystem (
open-pull-requests-limit: 1). - Template: examples/dependabot.yml.template
- Reviewing and merging PRs: skill
review-dependabot(changelog triage, risk tiers, rebase merge)
CI (GitHub Actions)
- PR dependency review: permissive
allow-licenses(not deprecateddeny-licenses); includeAGPL-3.0andAGPL-3.0-or-laterwhen the project is AGPL — template: examples/ci.yml.template
Tests and quality
| Need | Skill / tool |
|---|---|
| Unit / component tests | Vitest (always) |
| Cross-route, map, or auth flows | playwright-skill |
| Pure logic / utils only | Vitest — skip Playwright |
| Explicit deps / dead exports | knip.md · verify scripts package-json-scripts.md |