Angular Admin Dashboard Tutorial: Build from Scratch vs Use a Boilerplate
The Admin Dashboard Decision Every SaaS Founder Faces
I have built the same admin dashboard five times. Not because I enjoy it -- because every new SaaS project needed one, and every time I told myself "this time I will build it faster." The fifth time, I timed myself: 22 working days from ng new to a dashboard with auth, user management, billing, and analytics that I was not embarrassed to show customers. Twenty-two days of building features that exist in every SaaS on the planet. That is when I stopped building admin dashboards from scratch.
You have a backend API. You have paying customers who need a dashboard. Now you need to build the admin panel — user management, analytics, settings, billing, team invitations. The question is: do you build it from scratch, or do you start with a boilerplate?
This post compares both approaches with real Angular code, honest time estimates, and a clear recommendation based on where you are in your product journey.
What a Production Admin Dashboard Actually Requires
Before comparing approaches, let us list what a real SaaS admin dashboard needs:
- Authentication: Login, registration, password reset, optional 2FA
- Authorization: Role-based access (admin, member, viewer)
- Navigation: Sidebar, breadcrumbs, responsive layout
- User management: Invite, remove, change roles
- Organization settings: Name, logo, billing info
- Data tables: Sortable, filterable, paginated
- Analytics widgets: Charts, KPIs, trend indicators
- Billing page: Current plan, upgrade, invoices (see our Stripe integration guide)
- Profile settings: Name, email, avatar, password change
- Notifications: Toast messages, alert banners
- Loading states: Skeletons, spinners, error boundaries
That is not a weekend project. Let us see what each approach looks like.
Cloudrix SaaS Starter ships with this pre-configured and tested.
Skip weeks of boilerplate — auth, payments, multi-tenancy, and deployment included out of the box.
Try the live demo →Approach 1: Build from Scratch
Project Setup (Day 1)
ng new my-dashboard --routing --style=scss --standalone
ng add @angular/material
npm install @ngrx/store @ngrx/effects chart.js
Layout Component (Day 1-2)
Every dashboard starts with the shell — sidebar, toolbar, content area:
@Component({
selector: 'app-dashboard-layout',
standalone: true,
imports: [MatSidenavModule, MatToolbarModule, MatListModule, RouterOutlet],
template: `
<mat-sidenav-container class="dashboard-container">
<mat-sidenav mode="side" [opened]="!isMobile()" class="sidebar">
<mat-nav-list>
@for (item of navItems; track item.path) {
<a mat-list-item [routerLink]="item.path" routerLinkActive="active">
<mat-icon matListItemIcon>{{ item.icon }}</mat-icon>
<span>{{ item.label }}</span>
</a>
}
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<span>{{ pageTitle() }}</span>
<span class="spacer"></span>
<app-user-menu />
</mat-toolbar>
<main class="content">
<router-outlet />
</main>
</mat-sidenav-content>
</mat-sidenav-container>
`,
})
export class DashboardLayoutComponent {
isMobile = signal(window.innerWidth < 768);
pageTitle = signal('Dashboard');
navItems = [
{ path: '/dashboard', icon: 'dashboard', label: 'Overview' },
{ path: '/users', icon: 'people', label: 'Users' },
{ path: '/settings', icon: 'settings', label: 'Settings' },
{ path: '/billing', icon: 'payment', label: 'Billing' },
];
}
That gives you a sidebar. Now you need the overview page, which takes another day or two.
Data Table with Server-Side Pagination (Day 3-4)
The user management table is where complexity hits:
@Component({
selector: 'app-users-table',
standalone: true,
imports: [MatTableModule, MatPaginatorModule, MatSortModule, MatInputModule],
template: `
<mat-form-field appearance="outline">
<mat-label>Search users</mat-label>
<input matInput (input)="applyFilter($event)" />
</mat-form-field>
<table mat-table [dataSource]="dataSource" matSort (matSortChange)="onSort($event)">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let user">{{ user.name }}</td>
</ng-container>
<ng-container matColumnDef="email">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Email</th>
<td mat-cell *matCellDef="let user">{{ user.email }}</td>
</ng-container>
<ng-container matColumnDef="role">
<th mat-header-cell *matHeaderCellDef>Role</th>
<td mat-cell *matCellDef="let user">
<mat-chip [class]="user.role">{{ user.role }}</mat-chip>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let user">
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="editUser(user)">Edit</button>
<button mat-menu-item (click)="removeUser(user)">Remove</button>
</mat-menu>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="[10, 25, 50]" (page)="onPage($event)" />
`,
})
export class UsersTableComponent implements OnInit { ... }
This is just the table. You still need the invite dialog, role change logic, confirmation dialogs, error handling, and the API integration.
Authentication and Guards (Day 5-7)
Login page, registration flow, JWT interceptor, auth guard, token refresh logic, route protection. Each one is straightforward individually but together they take days. The authentication features in a SaaS need to handle OAuth, 2FA, and refresh tokens correctly:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
if (token) {
req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}
return next.handle(req).pipe(
catchError((error) => {
if (error.status === 401) {
return this.authService.refresh().pipe(
switchMap((newToken) => {
req = req.clone({
setHeaders: { Authorization: `Bearer ${newToken}` },
});
return next.handle(req);
}),
);
}
return throwError(() => error);
}),
);
}
}
The Honest Timeline for Building from Scratch
| Feature | Estimated Time |
|---|---|
| Project setup and layout | 2 days |
| Authentication flows | 3 days |
| User management | 3 days |
| Organization settings | 2 days |
| Billing integration | 3 days |
| Analytics dashboard | 2 days |
| Profile and notifications | 2 days |
| Polish and responsive design | 3 days |
| Total | ~20 working days |
That is four weeks of full-time work for one developer — and this assumes you do not hit any surprises. You will hit surprises.
Want to skip those 20 days? SaaS Starter includes a complete Angular admin dashboard with auth, user management, Stripe billing, org switching, and 55+ tests. Try the free lite version →
Approach 2: Use SaaS Starter
Setup (Minutes 0-15)
git clone https://github.com/niclas-niclas/saas-starter.git
cd saas-starter
cp .env.example .env
npm install
docker-compose up -d
npm run dev
Open localhost:4200. You have a working dashboard with authentication, user management, organization switching, role-based access, and a billing page. Fifteen minutes.
Customize (Hours 1-4)
Change the branding:
// Override theme variables
$primary: #your-brand-color;
$accent: #your-accent-color;
Add your first custom feature:
// Generate a new feature module
ng generate component features/my-feature --standalone
// Add it to the dashboard routes
{ path: 'my-feature', loadComponent: () => import('./features/my-feature/my-feature.component') }
The boilerplate provides the shell, authentication, authorization, API integration patterns, and common UI components. You add your domain-specific features on top.
What You Get Immediately
- Angular standalone components with Angular Material
- JWT authentication with refresh tokens
- Role-based access control with guards
- Multi-tenant organization switching
- User invitation and management
- Settings pages (profile, organization, billing)
- Responsive sidebar layout
- Toast notifications and loading states
- Server-side pagination patterns
- Dark mode support
The Real Comparison
| Criteria | From Scratch | With SaaS Starter |
|---|---|---|
| Time to first deploy | 4+ weeks | Same day |
| Authentication | Build it | Included |
| Multi-tenancy | Build it | Included |
| Billing UI | Build it | Included |
| Code quality | Depends on you | Established patterns |
| Learning | Maximum | Still high |
| Customization | Unlimited | Unlimited (it is your code) |
| Cost | Your time | Free (Lite) or licensed (Pro) |
When to Build from Scratch
Build from scratch if:
- Your product has zero standard SaaS features (no users, no teams, no billing)
- You are building this specifically to learn Angular deeply
- You have months of runway with no urgency to ship
When to Use a Boilerplate
Use a boilerplate if:
- You need to validate a product idea quickly
- You want to spend time on your unique features, not auth and CRUD
- You are a solo founder or small team
- You have done this before and do not want to do it again
Start with SaaS Starter
The free Lite version of SaaS Starter gives you the full Angular + NestJS dashboard architecture. No payment required, no trial expiration. Evaluate it, build a prototype, decide if the Pro features are worth it later.
See it live at demo.cloudrix.io — click through the dashboard, manage users, switch organizations. Everything you see is included in the boilerplate. Then clone it and start building the features that make your product unique instead of rebuilding the features every SaaS already has.
Pricing:
- Lite (Free) — Full dashboard architecture to evaluate
- Pro ($149) — Auth + billing + complete dashboard
- Business ($249) — Multi-tenancy + RBAC + advanced features
- Enterprise ($399) — Everything + priority support
View pricing | Try the live demo | Get the free lite version
Related posts: NestJS + Angular vs Next.js | Stripe Subscriptions guide | Deploy to AWS with Terraform