Skip to content

About

React 19 + TypeScript + Vite+ template: shadcn/ui Base UI (base-nova), Tailwind v4, RTK Query, Apollo 4, Keycloak, i18n, Vitest/Oxlint via vp

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

React Template — Vite+ / shadcn Base UI

A modern frontend service template built on the current toolchain:

  • Vite+ 1.4 (vp) — one binary for dev, build (Vite 8 + Rolldown), test (Vitest 5), lint (Oxlint) and format (Oxfmt); all configured in vite.config.ts
  • shadcn/ui base-nova style on Base UI (@base-ui/react 1.9), scaffolded with bunx --bun shadcn@latest init --preset b0 --template vite --base base
  • Tailwind CSS v4 (CSS-first, theme tokens in src/index.css, no tailwind.config.js)
  • React 19 + TypeScript strict, Redux Toolkit 2.13 (+ redux-persist), RTK Query, Apollo Client 4 (GraphQL), Keycloak 26 via a small own provider, react-router 7, i18next 26, react-hook-form + zod 4, TanStack Table v9, MSW 3
  • shadcn-ui/lint (@shadcn/lint) design-system rules running inside Oxlint — see vite.config.ts lint.rules

Commands (bun only)

Command Description
bun run dev Vite+ dev server on http://localhost:3000
bun run build tsc -b + production build (vp build)
bun run preview Serve the production build
bun run test Vitest once (vp test)
bun run coverage Vitest with V8 coverage
bun run check vp check — format + lint + typecheck in one pass
bun run lint / bun run format Oxlint / Oxfmt individually
bun run typecheck tsc --noEmit
bun run generate:graphql Regenerate src/graphql/generated/ from src/graphql/schema.graphql

Run a single test: bun run test -- src/App.test.tsx (or -t "name").

Local login

Start the backend stack first (spring-reactive-mongo-template dev-infra), then bun run dev.

Dependency URL
Keycloak (realm app, client frontend) http://localhost:8090
Backend REST /coroutine/*, GraphQL /graphql, SSE /update/user http://localhost:8080

Login via the user menu (top right). Real credentials live in the git-ignored .credentials file — copy .credentials.template and fill it in on a fresh clone. URLs come from the committed .env (all variables documented in .env.template; .env.production is generated by CI from repo secrets).

Live updates (/users) subscribe to GET /update/user with a bearer header — the client streams via fetch (no EventSource, no ticket, no credential in the URL). Reconnects always resolve a fresh token first (getAccessToken()), token_expired → refresh + reconnect, logged_out → the local session is expired, protected pages lock immediately and the stream never reconnects (see src/hooks/useSseUsers.ts).

Structure

src/
├── components/           # shared chrome: nav, side-menu, user-dropdown, cookie-consent,
│   │                     # mode-toggle, loading-screen, spinner, error-boundary
│   ├── permissions/      # header widget: permission CRUD in a Sheet via Apollo GraphQL;
│   │                     # usePermissionsGraphql.ts is the app's sole permission data layer
│   └── ui/               # generated shadcn (base-nova) — do not hand-edit
├── graphql/              # schema.graphql + generated/ (codegen client preset; git-ignored)
├── lib/                  # keycloak.ts, apollo.ts, utils (cn re-export)
├── locales/              # i18next init + en_US / pl_PL
├── pages/                # feature folders with colocated api/components/hooks/tests
│   ├── counter/          # RTK slice demo
│   ├── home/             # landing card
│   ├── error/            # 401 / 404 pages
│   └── users/            # RTK Query + SSE CRUD; owns its data-table,
│                         # roles combobox (data from components/permissions) and SSE status bar
├── providers/            # keycloak.tsx (own Keycloak context), theme.tsx (light/dark/system)
└── store/                # store + persisted counter slice + RTK Query api

UI kit — installed shadcn components

Generated into src/components/ui/ via bunx --bun shadcn@latest add <name> (registry: shadcn Base UI base-nova, preset b0). Never hand-edit; re-add or diff to update. This is the authoritative list:

Component Used by
badge sse-status-bar
button everywhere (nav, dialogs, table, counter, error-boundary…)
card home, counter, users, error-boundary
combobox roles-combobox (chips multi-select)
command — (scaffold default, currently unused)
dialog user-dialog
dropdown-menu user-dropdown-menu, mode-toggle, table row actions
field user-dialog form layout (the form component is a stub in this preset — use field + react-hook-form + zod)
input-group App header search; also a dependency of combobox
input App search, user-dialog, counter
label internally by field
navigation-menu App header, nav-item
popover — (installed, currently unused)
select data-table page-size picker
separator internally by field
sheet mobile side menu
sonner App <Toaster> + all toasts
table data-table
textarea internally by input-group

Add a component with bunx --bun shadcn@latest add <name> and update this table in the same change.

Docker

Dockerfile builds with oven/bun:1.4-slim (bun install → generate:graphql → vp build) and serves dist/ with nginx:1.30-alpine-slim (SPA fallback, hashed-asset caching, /health). Multi-arch (amd64/arm64) publishing runs in .github/workflows/docker.yml on main/release/* → docker.io/<org>/<repo>:latest.

Build-time config: VITE_APP_* are inlined from .env.production (CI creates it from repo secrets via SpicyPizza/create-envfile) — the deployed origin must be registered in the Keycloak client's redirect/web origins.

docker build -t react-typescript-vite-plus-template .
docker run --rm -p 3000:80 react-typescript-vite-plus-template

CI

  • .github/workflows/ci.yml — every push/PR: install → codegen → vp check (format + lint + typecheck) → tests → build
  • .github/workflows/docker.yml — main/release/*: tests first, then multi-arch image publish with GHA layer cache

Notes

  • bun run generate:graphql must run before build/test after cloning (generated code is git-ignored).
  • Formatting is Oxfmt (double quotes, 80 cols) — vp check --fix normalizes everything; re-run generate:graphql after formatting GraphQL document template literals.
  • src/components/ui/** is excluded from the shadcn design-system lint rules (components own their appearance); every other source file (src/components/*, src/providers, src/pages/**, src/lib) is fully linted.
  • vp test uses the test block in vite.config.ts (jsdom + globals + src/setupTests.ts).

License

MIT — see LICENSE.md.

About

React 19 + TypeScript + Vite+ template: shadcn/ui Base UI (base-nova), Tailwind v4, RTK Query, Apollo 4, Keycloak, i18n, Vitest/Oxlint via vp

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages