Appearance
Early Access
The early-access frontend combines a standalone /early-access landing page, an email signup form, and an optional launch-mode redirect from other marketing routes.
Route And Layout
The page is apps/marketing/src/app/(early-access)/early-access/page.tsx. Its route-group layout is separate from the main marketing shell and provides:
- the shared marketing stylesheet and local Manrope and Newsreader font files;
- the light browser color scheme;
- the TanStack Query provider and health check.
The page composes Confetti, Header, CTA, Preview, VisionBlurb, Footer, and early-access JSON-LD. It does not inherit the main site's navigation, footer, main-content wrapper, or toaster. The signup component renders its own light-themed toaster.
Structured preview and event-type data lives in apps/marketing/src/data/earlyAccess.ts. Repeated labels and form copy live under strings.earlyAccess in src/utils/strings.ts.
Signup Form
src/components/early-access/CTA.tsx is a client component. It uses React Hook Form and Zod to require a valid email address, then submits through the configured Tuyau React Query client:
tsx
const formSchema = z.object({
email: z
.string()
.min(1, strings.earlyAccess.form.validation.required)
.email(strings.earlyAccess.form.validation.invalid),
})
const subscribe = useMutation(api.subscriptions.subscribe.mutationOptions())
subscribe.mutate(
{ body: data },
{
onSuccess: () => {
toast.success(strings.earlyAccess.toast.success.title, {
description: strings.earlyAccess.toast.success.description,
})
form.reset()
},
onError: (error) => {
const message =
error instanceof Error
? error.message
: 'Failed to subscribe to the newsletter. Please try again.'
toast.error(message)
},
},
)While the mutation is pending, the button shows its submitting copy and is disabled. After success, the input and button remain disabled, the button shows the joined state, the form resets, and the page shows confirmation copy. Errors appear in a toast; Zod field errors appear through the shared form controls.
The request and response contract belongs in the Newsletter API Reference. Backend subscription and email behavior belongs in Newsletter Subscription Backend and Email Infrastructure Backend.
Launch Redirect
apps/marketing/next.config.ts reads ENABLE_GLOBAL_REDIRECTS when Next.js loads its configuration:
ts
const isGlobalRedirectsEnabled = process.env.ENABLE_GLOBAL_REDIRECTS === 'true'When true, redirects() returns a temporary redirect to /early-access for general marketing paths. When false or unset, it returns no redirect rules. The matcher excludes:
/early-access,/blogand descendants, and/studio;- Next.js assets under
/_next; - favicon, robots, sitemap, manifest, Open Graph, Twitter image, and icon routes;
- common static file extensions.
The exclusions keep the landing page, blog, framework assets, metadata, and public files reachable during launch mode. Change redirect behavior in next.config.ts, not in page components or client effects.
Frontend Configuration
The feature reads two marketing environment variables:
bash
ENABLE_GLOBAL_REDIRECTS=true
NEXT_PUBLIC_API_URL=https://api.jubiloop.localhostENABLE_GLOBAL_REDIRECTS controls the Next.js redirect rule. NEXT_PUBLIC_API_URL configures the Tuyau client used by the form. It is browser-visible and must contain only the public API origin.
Environment provisioning, secrets, deployment commands, and operational troubleshooting live in the Deployment Overview and Secrets Management pages.
Verification
Check the frontend behavior in both launch modes:
- With redirects enabled, a general marketing route redirects temporarily to
/early-access. /early-access,/blog, metadata routes, and static assets remain reachable.- With redirects disabled, normal marketing routes remain available and
/early-accessstill renders directly. - Invalid email input shows a field error without sending a request.
- A pending request disables repeat submission; success and failure produce the expected UI state.
- Cards, preview images, form controls, and confirmation feedback remain usable on mobile and with keyboard navigation.
Use the API and backend pages linked above when a failure occurs after the frontend sends the subscription request.