03 / Develop · Build · Frontend

Frontend ​

Layer 1 of six (the map) — and where the app is created, so start here. Got the four prerequisites in place? Then run along.

The baseline. veni create gives you a React app that federates into the Shell and is signed in for free — no login code, no second URL. Everything else in this section is a capability you add on top with veni enable.

bash
veni create calendar        # UI-only by default — API and database come later
Requirement

People open the Shell and find Calendar waiting there — already signed in, no second login, no separate URL to remember.

Resolving it. Unlike cloning an existing app, there's no cp .env.example .env here — veni create writes ui/.env for you, pointed at a local Shell:

bash
# calendar/ui/.env — written by veni create
VITE_API_URL=http://localhost:3014/api   # your API (once you `veni enable api`). Must end in /api
VITE_SHELL_API_URL=http://localhost:3000/api
VITE_SHELL_URL=http://localhost:5173     # Shell UI — the "← Shell" link and 401 redirects
VITE_PORT=4177
VITE_SERVICE_ID=calendar                 # the slug you register
Building against Dev instead? Edit these two by hand

veni register rewrites api/.env for whichever Shell you target — but it never touches ui/.env. Point the Shell lines at Dev yourself, or the UI keeps linking back to a localhost:5173 Shell that isn't running:

bash
VITE_SHELL_API_URL=https://shell-api.dev.venizia.ai/api
VITE_SHELL_URL=https://veni.dev.venizia.ai

We'll build it up simplest-first, starting with the month view. Run the UI — how depends on which Shell you picked:

bash
# The local Shell is itself a Vite dev server, so a dev-mode remote loads fine.
# `veni dev` runs from the app root and starts every service present — here, just the UI.
cd calendar && veni install && veni dev                 # UI → http://localhost:4177
bash
# The Dev Shell is a production build — serve one too. Same port, so the
# registered entry is unchanged. Re-run build after every UI change.
cd calendar/ui && bun install && bun run build && bun run preview   # http://localhost:4177
Against Dev: serve a build, and use Chrome

veni dev serves a dev remote — it carries an HMR shim and imports out of Vite's on-the-fly module graph, which a production host has no business loading. Build and preview instead; ui/.env is baked in at build time, so set it first.

Open the Dev Shell in Chrome: it's HTTPS loading your http://localhost:4177, which Chrome permits as a trusted origin and other browsers can refuse as mixed content — silently. Full detail in Cloud setup.

Register it so it shows up in the Shell (full options + flags). As a developer you register your own copy — from the CLI, or in the UI via Developer mode → My Apps (the developer role doesn't have admin's "Quick Integrate"):

bash
veni register --shell http://localhost:3000        # uses the PAT you installed with
bash
veni register --shell https://shell-api.dev.venizia.ai

Run it from the app root — veni register reads the slug and ports from the app's env. No PAT saved? Against a local Shell, add --user <you> to sign in with a password instead; Dev is SSO-only, so there a PAT is the only route. The PAT-based command is self-service, so it mounts at /<your-username>-calendar as a private draft you own (why); an admin login (--user <admin>) gives the unprefixed /calendar. Either way — build your calendar grid in ui/src.

Because it runs inside the Shell, it already knows the signed-in user: federation and the Shell's token broadcast are wired for you. So the calendar can greet them by name — and later show their events — with zero auth code. That's SSO, free.

Module Federation rules — do not break these

Expose exactly ./ShellEntry via federation/shell-entry.tsx, bundle your own React (shared: {}) and integrate through the mount pattern. Do not set manualChunks — it breaks the federated production build.

Done when — your calendar (/<you>-calendar, or /calendar if an admin registered it) loads inside the Shell and greets you by name: signed in, no second login.

Next: the month view needs events — an authenticated API and a database to hold them. Build the backend →

Stuck? See Common snags, or back to Build a satellite.