Introduction to Exo UI

Welcome to the official documentation for Exo UI — a beautifully crafted, fully accessible, and deeply customizable dashboard template system designed for modern web applications.

Exo UI is built from the ground up to provide teams with a robust starting point for creating analytics dashboards, SaaS admin panels, and complex data-driven layouts. By leveraging the utility-first power of Tailwind CSS, every single component from layout wrappers to fine-grained input controls can be adjusted to match your exact brand guidelines.

Whether you are spinning up a slick prototype or constructing an enterprise-scale architecture, this template delivers a meticulously organized codebase, built-in global dark mode support, fluid responsive design, and seamlessly integrated component libraries. This guide will walk you through the core development paradigms, theming architecture, and layout strategies you need to master Exo UI.

Installation

To get started with the template, install the dependencies and start the development server.

Terminal
# Install project dependencies
npm install

# Start the optimized Vite development server
ng serve

By default, the application will be available at http://localhost:4200.

Terminal
npm install
npm run dev
Terminal
npm install
npm run dev

Advanced Theming & Customization

Exo UI features a robust styling engine powered by Tailwind CSS v4 and native CSS variables. This creates a flexible system where updating a few core variables instantly trickles down to hundreds of UI components intuitively.

The Design Token Architecture

In src/styles.css, you will find `@layer base` defining standard CSS variables under the `:root` selector. These variables map directly to dynamic Tailwind utility classes like bg-primary or text-foreground.

src/styles.css
@layer base {
  :root {
    --background: #ffffff;
    --foreground: #09090b;
    --primary: #18181b;
    --primary-foreground: #fafafa;
    /* Adjust these to drastically recalculate the template's look! */
  }

  /* Seamless Dark Mode Inverses */
  .dark {
    --background: #09090b;
    --foreground: #fafafa;
    --primary: #fafafa;
    --primary-foreground: #18181b;
  }
}

Creating Architectural Themes

Exo UI ships with four color variants out of the box (`zinc`, `blue`, `rose`, `green`). Creating a brand new layout theme implies declaring a class globally overriding the core UI tokens.

/* Create a Custom Purple Theme */
.theme-purple {
  --primary: #9333ea;
  --ring: #9333ea;
}
.theme-purple.dark {
  --primary: #c084fc;
  --ring: #c084fc;
}

Central state management of the UI theme lives in src/app/core/services/theme.service.ts. It uses native Signals to maintain absolute reactive sync with the browser's DOM & memory.

Layouts & Architecture

Functional Standalone Routing

Exo UI relies entirely on modern Angular Standalone Components—meaning absolutely zero legacy ngModule overhead.

Open src/app/app.routes.ts to securely view and modify the primary routing tree structure. Application layouts are safely segregated at the structural root so authentication views are totally isolated from the logged-in dashboard logic.

src/app/app.routes.ts
export const routes: Routes = [
  {
    path: '',
    component: AppShellComponent,
    children: [
      {
        path: 'dashboard',
        loadComponent: () => import('./features/dashboard/dashboard.component').then(c => c.DashboardComponent)
      }
    ]
  },
  {
    path: 'login',
    component: AuthLayoutComponent // Completely unattached to main AppShell
  }
];

Component Sandboxing & Signal Forms API

  • All generic presentational frontend components sit inside src/app/shared/components/. They strictly rely on `input()` API implementations over old `@Input()` decorators.
  • Exo UI leverages the bleeding-edge Angular Signal forms APIs combined with `Zod` validation schemas for user input payloads. Forms are strongly typed, fully reactive, and completely boilerplate-free natively.

React Router Architecture

In React, routing is configured using modern react-router-dom strategies leveraging nested outlet paths.

Vue Router Architecture

In Vue, view structures are managed cleanly utilizing standard nested routing strategies.

Typography

Exo UI relies on a standard system font stack to ensure zero-latency layout loading and maximum OS-level text crispness. We utilize utility classes like `text-sm`, `font-semibold`, and `tracking-tight` carefully to map directly back to Tailwind.

Heading 1

class="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl"

Heading 2

class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0"

Iconography

Exo UI defaults to the Lucide Icons set. Being exclusively SVG based, they are highly accessible, perform crisp scaling, and bypass the payload weight required by large icon fonts.

Button

A flexible button component with multiple variants, sizes, and states like loading icons.

<app-button variant="default">Default</app-button>
<app-button variant="destructive">Destructive</app-button>
<app-button variant="outline">Outline</app-button>
<app-button variant="ghost">Ghost</app-button>

<!-- With Icon -->
<app-button>
  <svg lucideIcon="mail" class="mr-2 h-4 w-4"></svg>
  With Icon
</app-button>

<!-- Loading State -->
<app-button disabled>
  <svg lucideIcon="loader-2" class="mr-2 h-4 w-4 animate-spin"></svg>
  Processing
</app-button>
<Button variant="default">Default</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>

{/* With Icon */}
<Button>
  <Mail className="mr-2 h-4 w-4" />
  With Icon
</Button>

{/* Loading State */}
<Button disabled>
  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
  Processing
</Button>
<Button variant="default">Default</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>

<!-- With Icon -->
<Button>
  <MailIcon class="mr-2 h-4 w-4" />
  With Icon
</Button>

<!-- Loading State -->
<Button disabled>
  <Loader2Icon class="mr-2 h-4 w-4 animate-spin" />
  Processing
</Button>

Card

Versatile container elements bridging content, actions, and layouts.

Standard Layout

Project Summary

Monitor your deployments.

Your total deployments ran successfully. Please check the logs.
Media Header

Image Card

A card featuring rich media inside.

<!-- Standard Card -->
<app-card class="w-full max-w-sm">
  <app-card-header>
    <app-card-title>Project Summary</app-card-title>
    <app-card-description>Monitor your deployments.</app-card-description>
  </app-card-header>
  <app-card-content class="text-sm">
    Your total deployments ran successfully.
  </app-card-content>
</app-card>

<!-- Action Card with Image Placeholder -->
<app-card class="w-full max-w-sm">
  <div class="h-32 bg-gray-100 rounded-t-xl"></div>
  <app-card-header>
    <app-card-title>Image Card</app-card-title>
    <app-card-description>Rich media content</app-card-description>
  </app-card-header>
  <app-card-footer>
    <app-button class="w-full">View Gallery</app-button>
  </app-card-footer>
</app-card>
{/* Standard Card */}
<Card className="w-full max-w-sm">
  <CardHeader>
    <CardTitle>Project Summary</CardTitle>
    <CardDescription>Monitor your deployments.</CardDescription>
  </CardHeader>
  <CardContent className="text-sm text-gray-600">
    Your total deployments ran successfully.
  </CardContent>
</Card>

{/* Action Card */}
<Card className="w-full max-w-sm">
  <div className="h-32 bg-gray-100 rounded-t-xl" />
  <CardHeader>
    <CardTitle>Image Card</CardTitle>
    <CardDescription>Rich media content</CardDescription>
  </CardHeader>
  <CardFooter>
    <Button className="w-full">View Gallery</Button>
  </CardFooter>
</Card>
<!-- Standard Card -->
<Card class="w-full max-w-sm">
  <CardHeader>
    <CardTitle>Project Summary</CardTitle>
    <CardDescription>Monitor your deployments.</CardDescription>
  </CardHeader>
  <CardContent class="text-sm text-gray-600">
    Your total deployments ran successfully.
  </CardContent>
</Card>

<!-- Action Card -->
<Card class="w-full max-w-sm">
  <img src="..." class="h-32 rounded-t-xl object-cover" />
  <CardHeader>
    ...
  </CardHeader>
  <CardFooter>
    <Button class="w-full">View Gallery</Button>
  </CardFooter>
</Card>

Badge

Highlights status, labels, counts, or categorization metrics.

Primary
Secondary
Destructive
Outline
Success
<app-badge variant="default">Primary</app-badge>
<app-badge variant="secondary">Secondary</app-badge>
<app-badge variant="destructive">Destructive</app-badge>
<app-badge variant="outline">Outline</app-badge>
<!-- Custom Class Injection -->
<app-badge class="bg-green-100 text-green-700">Success</app-badge>
<Badge variant="default">Primary</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>
{/* Custom Extension via Tailwind */}
<Badge className="bg-green-100 text-green-700">Success</Badge>
<Badge variant="default">Primary</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>

Alerts

Display a callout for user attention highlighting states or errors.

Default Alert
A new update is available. Adding additional features implicitly.
Destructive Alert
Your session has expired. Please log in again to continue working.
Success Message
Your payment was completed successfully!
<!-- Default -->
<app-alert>
  <svg lucideIcon="info" class="h-4 w-4"></svg>
  <app-alert-title>Information</app-alert-title>
  <app-alert-description>A new update is available.</app-alert-description>
</app-alert>

<!-- Destructive -->
<app-alert variant="destructive">
  <svg lucideIcon="alert-triangle" class="h-4 w-4"></svg>
  <app-alert-title>Error</app-alert-title>
  <app-alert-description>Your session has expired.</app-alert-description>
</app-alert>

<!-- Success (Custom Override) -->
<app-alert class="bg-green-50 border-green-200 text-green-700">
  <svg lucideIcon="check" class="h-4 w-4 text-green-600"></svg>
  <app-alert-title>Success</app-alert-title>
  <app-alert-description>Paid successfully!</app-alert-description>
</app-alert>
{/* Default Alert */}
<Alert>
  <InfoIcon className="h-4 w-4" />
  <AlertTitle>Default Alert</AlertTitle>
  <AlertDescription>A new update is available.</AlertDescription>
</Alert>

{/* Destructive Alert */}
<Alert variant="destructive">
  <AlertTriangleIcon className="h-4 w-4" />
  <AlertTitle>Destructive Alert</AlertTitle>
  <AlertDescription>Your session has expired.</AlertDescription>
</Alert>

{/* Success Override */}
<Alert className="bg-green-50 border-green-200 text-green-700">
  <CheckIcon className="h-4 w-4 text-green-600" />
  <AlertTitle>Success Message</AlertTitle>
  <AlertDescription>Completed!</AlertDescription>
</Alert>
<Alert variant="destructive">
  <AlertTriangle class="h-4 w-4" />
  <AlertTitle>Warning</AlertTitle>
  <AlertDescription>Disk space low.</AlertDescription>
</Alert>

Avatar Elements

Profiles representing users or resources interchangeably with auto-fallbacks natively managed.

Fallback
AF
Profile Photo Avatar
Sizes
X
Y
Z
Overlap Group
+3
<!-- Basic Fallback -->
<app-avatar>
  <app-avatar-fallback>AF</app-avatar-fallback>
</app-avatar>

<!-- Fully Rendered Image -->
<app-avatar>
  <app-avatar-image src="https://i.pravatar.cc/150?u=v" alt="Profile" />
  <app-avatar-fallback>AF</app-avatar-fallback>
</app-avatar>

<!-- Avatar Group Cluster -->
<div class="flex -space-x-4">
  <app-avatar class="border-2 border-white">
    <app-avatar-image src="https://i.pravatar.cc/150?u=a" />
  </app-avatar>
  <app-avatar class="border-2 border-white">
    <app-avatar-image src="https://i.pravatar.cc/150?u=b" />
  </app-avatar>
  <app-avatar class="border-2 border-white">
    <app-avatar-fallback>+3</app-avatar-fallback>
  </app-avatar>
</div>
{/* Fully Rendered Formats */}
<Avatar className="h-12 w-12">
  <AvatarImage src="https://i.pravatar.cc/150?u=v" alt="Avatar" />
  <AvatarFallback>AF</AvatarFallback>
</Avatar>

{/* Group Avatar Mapping Tool */}
<div className="flex -space-x-4">
  <Avatar className="h-10 w-10 border-2 border-background">
    <AvatarImage src="https://i.pravatar.cc/150?u=a" />
  </Avatar>
  <Avatar className="h-10 w-10 border-2 border-background">
    <AvatarImage src="https://i.pravatar.cc/150?u=b" />
  </Avatar>
  <Avatar className="h-10 w-10 border-2 border-background">
    <AvatarFallback>+3</AvatarFallback>
  </Avatar>
</div>
<Avatar class="h-12 w-12">
  <AvatarImage src="https://i.pravatar.cc/150?u=v" alt="Avatar" />
  <AvatarFallback>AF</AvatarFallback>
</Avatar>

Progress Indicator

Displays completion progress across upload stages, or generalized status loading bars.

Loading Module... 60%
File Upload Complete
100%
<!-- Default Progress -->
<app-progress [value]="60" class="w-full" />

<!-- With Labeling Overrides -->
<div class="w-full max-w-sm">
  <div class="mb-1 flex justify-between text-sm">
    <span class="font-medium">Uploading...</span>
    <span class="text-muted-foreground">{{percent}}%</span>
  </div>
  <app-progress [value]="percent" />
</div>
<Progress value={60} className="w-full max-w-sm" />

{/* Colored Completeness Mapping */}
<div className="w-full max-w-md">
  <div className="mb-2 flex justify-between text-sm items-center">
    <span className="font-medium">File Upload Complete</span>
    <span className="text-green-600">100%</span>
  </div>
  {/* Apply bespoke green classes via tailored CN injection */}
  <Progress value={100} className="h-1" indicatorClassName="bg-green-500" />
</div>
<Progress :value="60" class="w-full max-w-sm" />

Accordion

A vertically stacked set of interactive headings that expand to reveal associated complex sections.

Yes, it adapts flawlessly to mobile screens. Leveraging direct Tailwind viewport blocks gracefully.
<app-accordion type="single" collapsible class="w-full max-w-lg">
  <app-accordion-item value="item-1">
    <app-accordion-trigger>Is the layout responsive?</app-accordion-trigger>
    <app-accordion-content>
      Yes, it adapts flawlessly to mobile screens.
    </app-accordion-content>
  </app-accordion-item>

  <app-accordion-item value="item-2">
    <app-accordion-trigger>Can I use React with this?</app-accordion-trigger>
    <app-accordion-content>
      Yes, natively integrated.
    </app-accordion-content>
  </app-accordion-item>

  <app-accordion-item value="item-3" disabled>
    <app-accordion-trigger>Disabled Item</app-accordion-trigger>
  </app-accordion-item>
</app-accordion>
<Accordion type="single" collapsible className="w-full max-w-lg">
  <AccordionItem value="item-1">
    <AccordionTrigger>Is the layout responsive?</AccordionTrigger>
    <AccordionContent>
      Yes, it adapts flawlessly to mobile screens.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem value="item-2">
    <AccordionTrigger>Can I use React with this?</AccordionTrigger>
    <AccordionContent>
      Absolutely!
    </AccordionContent>
  </AccordionItem>

  <AccordionItem value="item-3" disabled>
    <AccordionTrigger>Disabled Item</AccordionTrigger>
  </AccordionItem>
</Accordion>
<Accordion type="multiple" collapsible class="w-full max-w-lg">
  <AccordionItem value="item-1">
    <AccordionTrigger>Is the layout responsive?</AccordionTrigger>
    <AccordionContent>
      Yes, it adapts flawlessly to mobile screens.
    </AccordionContent>
  </AccordionItem>
</Accordion>

Timeline

A linear chronological vertical layout for logging activity feeds and events sequentially.

Ordered Placed
Processing
Delivery
<app-timeline>
  <app-timeline-item status="completed">
    <app-timeline-dot class="bg-green-500" />
    <app-timeline-content>
      <div class="font-medium">Order Placed</div>
      <time class="text-xs text-muted-foreground">10:24 AM</time>
    </app-timeline-content>
  </app-timeline-item>

  <app-timeline-item status="current">
    <app-timeline-dot class="bg-blue-500" />
    <app-timeline-content>
      <div class="font-medium">Processing</div>
    </app-timeline-content>
  </app-timeline-item>

  <app-timeline-item status="pending">
    <app-timeline-dot />
    <app-timeline-content>
      <div class="font-medium">Delivery</div>
      <time class="text-xs text-muted-foreground">Pending</time>
    </app-timeline-content>
  </app-timeline-item>
</app-timeline>
<Timeline>
  <TimelineItem>
    <TimelineSeparator>
      <TimelineDot className="bg-green-500 text-white">
        <CheckIcon className="h-4 w-4" />
      </TimelineDot>
      <TimelineConnector />
    </TimelineSeparator>
    <TimelineContent>
      <div className="font-medium text-sm">Order Placed</div>
      <time className="text-xs text-muted-foreground">10:24 AM</time>
    </TimelineContent>
  </TimelineItem>

  <!-- Multiple Steps Added Here -->
</Timeline>
<Timeline>
  <TimelineItem>
    ...
  </TimelineItem>
</Timeline>

Form Architecture

Complete standard input integrations featuring reactive validation wrappers utilizing Zod APIs.

We'll never share your email closely.

Username is already taken.

<form [formGroup]="profileForm" class="space-y-6 max-w-md">

  <!-- Basic Input -->
  <div class="space-y-2">
    <label class="text-sm font-medium">Email</label>
    <input formControlName="email" class="input-class" placeholder="[email protected]" />
    <p class="text-xs text-muted-foreground">We'll never share your email closely.</p>
  </div>

  <!-- Validation Errors Wrapper -->
  <div class="space-y-2">
    <label class="text-sm font-medium text-destructive">Username</label>
    <input formControlName="username" class="input-class border-destructive" />
    @if (profileForm.controls.username.errors?.['taken']) {
      <p class="text-xs text-destructive font-medium">Username is already taken.</p>
    }
  </div>

  <!-- Checkbox Component -->
  <div class="flex items-center space-x-2">
     <app-checkbox formControlName="terms" id="terms"></app-checkbox>
     <label for="terms" class="text-sm font-medium leading-none">Accept terms</label>
  </div>

  <app-button type="submit" class="w-full">Submit Profile</app-button>

</form>
<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 w-full max-w-md">

    <FormField
      control={form.control}
      name="email"
      render={({ field }) => (
        <FormItem>
          <FormLabel>Email Address</FormLabel>
          <FormControl>
            <Input placeholder="[email protected]" {...field} />
          </FormControl>
          <FormDescription>We'll never share your email.</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />

    <FormField
      control={form.control}
      name="terms"
      render={({ field }) => (
        <FormItem className="flex flex-row items-start space-x-3 space-y-0 p-4 border rounded-md">
          <FormControl>
            <Checkbox checked={field.value} onCheckedChange={field.onChange} />
          </FormControl>
          <div className="space-y-1 leading-none">
            <FormLabel>Accept terms and conditions</FormLabel>
          </div>
        </FormItem>
      )}
    />

    <Button type="submit" className="w-full">Submit Profile</Button>

  </form>
</Form>
<form @submit="onSubmit" class="space-y-6 max-w-md">
  <FormField name="email" v-slot="{ componentField }">
    <FormItem>
      <FormLabel>Email</FormLabel>
      <FormControl>
        <Input type="email" v-bind="componentField" />
      </FormControl>
      <FormMessage />
    </FormItem>
  </FormField>
</form>

Data Tables

Complex, responsive tables for displaying high-density database collections with built-in alignments.

Product name Color Category Price
Apple MacBook Pro 17" Silver Laptop $2999
Microsoft Surface Pro White Laptop PC $1999
Magic Mouse 2 Black Accessories $99
 {/* Standard JSX Table Implementation */}
<div className="w-full border rounded-lg overflow-hidden">
  <table className="w-full text-sm text-left text-gray-500">
    <thead className="text-xs uppercase bg-gray-50 border-b">
      <tr>
        <th className="px-6 py-3 font-medium">Product name</th>
        <th className="px-6 py-3 font-medium text-right">Price</th>
      </tr>
    </thead>
    <tbody>
      <tr className="bg-white border-b hover:bg-gray-50 transition-colors">
        <td className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">MacBook Pro</td>
        <td className="px-6 py-4 text-right font-medium text-gray-900">$2999</td>
      </tr>
    </tbody>
  </table>
</div>

Modals & Dialogs

Overlay patterns for interrupting user workflows safely requiring immediate action or confirmations.

Are you absolutely sure?

This action cannot be undone. This will permanently delete your account and remove your data from our servers.

<Dialog>
  <DialogTrigger asChild>
    <Button variant="outline">Delete Account</Button>
  </DialogTrigger>
  <DialogContent className="sm:max-w-[425px]">
    <DialogHeader>
      <DialogTitle>Are you absolutely sure?</DialogTitle>
      <DialogDescription>
        This action cannot be undone.
      </DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <Button variant="ghost">Cancel</Button>
      <Button variant="destructive">Delete Account</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Dropdown Menus

Contextual menus bridging vast feature sets into small spaces, usually mounted to buttons or avatars.

My Account
Profile ⇧⌘P
Billing ⌘B
Team Settings
Log out
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="outline">Open Menu</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent className="w-56">
    <DropdownMenuLabel>My Account</DropdownMenuLabel>
    <DropdownMenuSeparator />
    <DropdownMenuGroup>
      <DropdownMenuItem>
        Profile
        <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
      </DropdownMenuItem>
      <DropdownMenuItem>
        Billing
        <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
      </DropdownMenuItem>
    </DropdownMenuGroup>
    <DropdownMenuSeparator />
    <DropdownMenuItem className="text-red-600">
      Log out
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

Tabs

Horizontal mapping structures letting users rapidly flip between grouped data sets without losing structural context.

Account Settings

Make changes to your account here. Click save when you're done.

<Tabs defaultValue="account" className="w-[400px]">
  <TabsList className="grid w-full grid-cols-2">
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
  </TabsList>
  <TabsContent value="account">
    <Card>
      <CardHeader>
        <CardTitle>Account</CardTitle>
        <CardDescription>Make changes here.</CardDescription>
      </CardHeader>
      <CardContent>...</CardContent>
    </Card>
  </TabsContent>
</Tabs>

Skeleton Loaders

Visual mapping geometries informing the user that deep data sets are actively stream processing into the view.

<div className="flex items-center space-x-4">
  <Skeleton className="h-12 w-12 rounded-full" />
  <div className="space-y-2">
    <Skeleton className="h-4 w-[250px]" />
    <Skeleton className="h-4 w-[200px]" />
  </div>
</div>

Dashboard Layout

The primary AppShellComponent creates a scaffold housing the sticky top-navigation header, collapsible left sidebar, and dynamically rendered Router Outlet nested content area.

Sticky Header
Sidebar Nav
<router-outlet> Main Module

Auth Layout

For Login, Registration, and Forgotten Password pages, Exo UI provides an isolated AuthLayoutComponent wrapper. It purposefully ships completely stripped of the sidebar and internal navigations.

Routing & Guarding

Navigation in Exo UI relies on nested router configurations to handle the `AppShell` wrappers completely independently from the authentication states.

React Router Setup

<Routes>
  {/* Auth Layout Wrapper */}
  <Route element={<AuthLayout />}>
    <Route path="/login" element={<Login />} />
    <Route path="/signup" element={<Signup />} />
  </Route>

  {/* Nested App Dashboard */}
  <Route path="/" element={<AppShell />}>
    <Route index element={<Dashboard />} />
    <Route path="analytics" element={<Analytics />} />
  </Route>
</Routes>

For Angular, use the standard `RouterModule.forRoot()` wrapping identical `children` arrays attached to independent Layout Components.

Deployment Best Practices

Exo UI compiles down strictly to static SPA bundles (`dist` or `out`). When deploying into production pipelines like Vercel, Netlify, or AWS S3, you must configure your server routing rules to fallback to `index.html` securely!

  • Vercel: Zero-conf deployment via Vite or Angular CLI detection implicitly rewrites traffic.
  • Netlify: Explicitly add a _redirects file mapping /* /index.html 200.
  • AWS Cloudfront: Apply Custom Error Responses capturing 404 signals and safely dropping them into 200 index executions.