Songbird React UI (react_ui/)
The React frontend provides the full DAW interface for Songbird. It runs inside a JUCE WebView (not a regular browser) and communicates with the C++ backend via native functions.
Tech Stack
- React 19 — UI framework
- Vite — Build tool with
@vitejs/plugin-react-swc - TypeScript — Type safety
- Tailwind CSS v4 — Utility-first styling (via
@tailwindcss/vite) - Zustand — Lightweight state management with persistence
- Radix UI — Accessible primitive components
- class-variance-authority — Component variant styling
- @tonaljs — Music theory (chords, notes, scales)
Project Structure
Build & Development
Important: In production, the React app runs inside the JUCE WebView, not a browser. Thewindow.__JUCE__global is injected by JUCE and provides the native function bridge. When runningnpm run dev, bridge calls are no-ops.
WebView Embedding
The C++ backend loads the built React app fromreact_ui/dist/ via the JUCE WebBrowserComponent. Key implications:
- No browser APIs:
localStorage,fetchto external URLs, and other browser-specific APIs are unavailable or restricted - State persistence: All persistence routes through
juceBridge(Zustand ↔ C++ ↔ disk) - Native functions: Use
nativeFunction('name')from@/data/bridgeinstead of HTTP requests - Events: C++ pushes data via
emitEventIfBrowserIsVisible()→addStateListener()in JS
Key Conventions
- Atomic Design: Components organized as atoms → molecules → organisms → panels
- Tailwind-only styling: No CSS modules or styled-components. Use
cn()from@/lib/utilsfor conditional classes - Zustand for state: Never use React Context or prop drilling for shared state
- Direct DOM for meters: High-frequency visuals (level meters, playheads) use
subscribeRtBuffer()+ direct DOM manipulation, bypassing React’s render cycle - Path aliases:
@/maps tosrc/(configured intsconfig.app.json)