Skip to content

React & shadcn/ui

The default frontend in the AI-Driven Development Model is built on React with shadcn/ui as the component foundation — a deliberate alternative to the classic SAP Fiori / UI5 stack. When Fiori design compliance is mandated instead, see UI5 Web Components.

Stack

UI

  • shadcn/ui (Base UI primitives) + Tailwind CSS v4 — default for modern product-style apps.
    • components.json"style": "base-*" (e.g. base-vega); Base UI is the shadcn default since July 2026.
    • Primitive package: @base-ui-components/react (replaces the many @radix-ui/react-* packages).
    • Use the render prop — never asChild (Base UI has no asChild).
    • checked is strictly boolean; use the indeterminate param for partial states.
    • Select maps differently than Radix (Popup/Positioner, not Content/Viewport) — not a 1:1 import swap.
    • Non-interactive shadcn init in CI must pass -b radix if Radix is intentionally required.

AI-assisted shadcn tooling

This project ships a dedicated shadcn MCP server plus matching agent skills, so the AI agent can:

  • Search for relevant components directly against the shadcn registry instead of guessing names or hand-browsing docs.
  • Scaffold with the right commands — add components via the correct shadcn CLI invocation.
  • Apply best practices out of the box — the skills encode the Base UI conventions above (render instead of asChild, boolean checked, Popup/Positioner mapping), so generated code follows them without repeated prompting.

See AI-Driven Workflow → Agents & Tooling for how this fits the overall model.

Fiori Building Blocks (annotation-driven)

For SAP-style transactional apps, the model ships a set of React Fiori components (packages/ui-core/components/fiori/) that render CAP's Fiori annotations out of the box — the React/shadcn counterpart to SAP Fiori elements, without UI5:

  • Building blocksPage (list-report-style list + table), ObjectPage, ScopePage, FioriTable, Field, DataPoint.
  • Metadata-driven — an ODataMetadataProvider (basePath="/odata/v4/<domain>/<service>") reads the OData $metadata incl. the @UI.* annotations; useODataMetadata plus generic entity hooks (useEntityData, useInfiniteEntityData) bind any annotated entity with no per-field code.
  • Value helps@Common.ValueList annotations (Wertehilfen) render as searchable pickers automatically.
  • Fiori-bound features — a feature is wired to its CAP service by placing the ODataMetadataProvider in the package's Intent; generic hooks then resolve columns, facets, fields, and value helps from the metadata.

This is the annotation-driven productivity of Fiori elements delivered in the modern shadcn stack. It is distinct from UI5 Web Components: you get Fiori building blocks and floorplans (list report, object page) in shadcn styling — not the UI5-based Fiori design itself.

Data Access

  • API abstraction: TanStack Query v5 (@tanstack/react-query) for all OData v4 and REST calls.
    • There is no React Query v6 — v6 exists only as a Svelte adapter; do not generate v6-style APIs.
    • odata-query additionally for complex filter / paging / expand.
    • ⚠️ CAP v10: Decimal and Int64 arrive as strings in OData responses ("9.99", not 9.99) — parse/type accordingly and avoid strict numeric equality on these fields.

Data Tables

  • TanStack Table v8 (stable) + TanStack Virtual for all sortable, filterable, paginated tables.
    • TanStack Virtual for row/column virtualization on large datasets (> ~200 rows rendered).
    • Pin to v8 — v9 is in beta with a substantially different API (useTable instead of useReactTable, tree-shakable _features / _rowModels plugins); do not mix v8 and v9 patterns.
    • When adopting v9 later: run npx @tanstack/table intent add for version-matched agent skills, and use useLegacyTable only as a temporary migration aid.

State Management

  • TanStack Query — server state.
  • Zustand v5 — UI / client state.
    • Multi-value selectors must use useShallow from zustand/shallow — selectors returning fresh object/array references cause infinite render loops in v5.

i18n

Default Library Choices

Core decisions above are binding; the following are defaults for common concerns. Deviating is fine, but document the reason in the project's own docs.

ConcernDefaultNotes
FormsReact Hook Form + Zodshadcn/ui form components are built on RHF; Zod v4 has different imports/APIs than v3 — do not mix
IconsLucide (lucide-react)shadcn/ui default icon set
Toasts / notificationsSonnershadcn/ui recommended toast
ChartsRechartsshadcn/ui chart components are Recharts-based
Node-based UIs / flow editorsReact Flow (@xyflow/react)For graph/node editors — not for statistical charts (use Recharts)
Drag & dropdnd-kitStandard choice; Pragmatic Drag and Drop is the fallback if a blocker arises
Positioning / popoversFloating UI (@floating-ui/react)Base UI's positioning engine; use directly only for custom positioned components — Base UI components handle it internally
Code editorCodeMirror 6Lightweight, modular, tree-shakable; Monaco Editor only for heavy needs (full IDE features, TS language service) and always lazy-loaded
AnimationsMotion (motion package)Formerly Framer Motion — import from motion/react, not framer-motion
Datesdate-fnsTree-shakable; wire locales to the i18next language
RoutingReact Router v7library modeFramework mode conflicts with our Vite + Module Federation setup; evaluate TanStack Router for new standalone apps
Offline cache persistence@tanstack/react-query-persist-client + @tanstack/query-async-storage-persisterPersist the TanStack Query cache for offline use via PersistQueryClientProvider. Back the async persister with IndexedDB through idb-keyval (get/set/del satisfy the AsyncStorage interface — avoid localStorage for larger caches). Tune gcTime/maxAge (e.g. 24 h); resume offline writes with queryClient.resumePausedMutations()
PWA / service workervite-plugin-pwa (Workbox)Web App Manifest injection, service worker with offline precache + runtime caching, auto-update (stale-while-revalidate). Pairs with the query persistence above for a fully offline-capable app