Hero sections set the tone for an entire page. Here are the Angular patterns I use for landing pages and marketing sites โ each built as a reusable standalone component.
Generic Hero Component
// hero.component.ts import { Component, Input } from '@angular/core' import { NgIf } from '@angular/common' export interface HeroConfig { badge?: string headline: string highlight?: string // word(s) in headline to color differently subheadline: string primaryCta: { label: string; href: string } secondaryCta?: { label: string; href: string } stats?: { value: string; label: string }[] backgroundImage?: string dark?: boolean } @Component({ selector: 'app-hero', standalone: true, imports: [NgIf], template: ` <section class="position-relative py-5" [class.text-white]="config.dark" [style.background]="config.dark ? 'linear-gradient(135deg,#0d0d0d 0%,#1a1a2e 100%)' : '#fff'" [style.min-height.px]="500" > <!-- Background image overlay --> <div *ngIf="config.backgroundImage" class="position-absolute top-0 start-0 w-100 h-100" [style.background-image]="'url(' + config.backgroundImage + ')'" [style.background-size]="'cover'" [style.background-position]="'center'" [style.opacity]="0.15" ></div> <div class="container position-relative py-5"> <div class="row justify-content-center text-center"> <div class="col-lg-8"> <!-- Badge --> <span *ngIf="config.badge" class="badge px-3 py-2 mb-3 fw-semibold" style="background:rgba(253,71,102,0.15);color:#fd4766;font-size:0.8rem;"> {{ config.badge }} </span> <!-- Headline --> <h1 class="display-4 fw-bold mb-3" [class.text-white]="config.dark"> <ng-container *ngIf="config.highlight; else plainHeadline"> {{ headlineBefore }} <span style="color:#fd4766;">{{ config.highlight }}</span> {{ headlineAfter }} </ng-container> <ng-template #plainHeadline>{{ config.headline }}</ng-template> </h1> <!-- Subheadline --> <p class="fs-5 mb-4 mx-auto" [class.text-secondary]="config.dark" [class.text-muted]="!config.dark" style="max-width:560px;" > {{ config.subheadline }} </p> <!-- CTAs --> <div class="d-flex gap-3 justify-content-center flex-wrap mb-5"> <a [href]="config.primaryCta.href" class="btn btn-lg px-4 fw-semibold text-white" style="background:#fd4766;border:none;"> {{ config.primaryCta.label }} </a> <a *ngIf="config.secondaryCta" [href]="config.secondaryCta.href" class="btn btn-lg px-4 fw-semibold" [class.btn-outline-light]="config.dark" [class.btn-outline-secondary]="!config.dark" > {{ config.secondaryCta.label }} </a> </div> <!-- Stats --> <div *ngIf="config.stats" class="d-flex gap-4 justify-content-center flex-wrap"> <div *ngFor="let stat of config.stats; let i = index" class="d-flex align-items-center gap-3"> <div *ngIf="i > 0" class="border-start border-secondary ps-4 opacity-50"></div> <div> <div class="fw-bold fs-4" [class.text-white]="config.dark">{{ stat.value }}</div> <div class="small" [class.text-secondary]="config.dark" [class.text-muted]="!config.dark">{{ stat.label }}</div> </div> </div> </div> </div> </div> </div> </section> ` }) export class HeroComponent { @Input() config!: HeroConfig get headlineBefore(): string { if (!this.config.highlight) return this.config.headline const idx = this.config.headline.indexOf(this.config.highlight) return idx > -1 ? this.config.headline.slice(0, idx) : this.config.headline } get headlineAfter(): string { if (!this.config.highlight) return '' const idx = this.config.headline.indexOf(this.config.highlight) return idx > -1 ? this.config.headline.slice(idx + this.config.highlight.length) : '' } }
Usage in a Page Component
// landing-page.component.ts import { Component } from '@angular/core' import { HeroComponent, HeroConfig } from './hero.component' @Component({ selector: 'app-landing', standalone: true, imports: [HeroComponent], template: `<app-hero [config]="heroConfig" />` }) export class LandingPageComponent { heroConfig: HeroConfig = { badge: '๐ Angular 22 + Bootstrap 5', headline: 'Admin Templates Built for Angular Developers', highlight: 'Angular Developers', subheadline: 'Production-ready dashboards with signals, standalone components and Bootstrap 5. Get 30% off with code FIRST30.', primaryCta: { label: 'Browse Templates โ', href: 'https://lettstartdesign.com' }, secondaryCta: { label: 'Free Components', href: '/angular' }, stats: [ { value: '50+', label: 'Components' }, { value: '4.9โ ', label: 'Rating' }, { value: '12K+', label: 'Downloads' }, ], dark: true, } }
Split Layout Hero
For product pages, a two-column hero with text left and visual right works well:
// split-hero.component.ts import { Component, Input } from '@angular/core' import { NgIf, NgFor } from '@angular/common' @Component({ selector: 'app-split-hero', standalone: true, imports: [NgIf, NgFor], template: ` <section class="py-5" style="background:#0d0d0d;min-height:520px;"> <div class="container py-4"> <div class="row align-items-center g-5"> <!-- Left: copy --> <div class="col-lg-6"> <span class="badge px-3 py-2 mb-3 fw-semibold d-inline-block" style="background:rgba(253,71,102,0.15);color:#fd4766;"> Angular 22 + Bootstrap 5 </span> <h1 class="display-5 fw-bold text-white mb-3"> {{ headline }}<br> <span style="color:#fd4766;">{{ accentLine }}</span> </h1> <p class="text-secondary fs-5 mb-4">{{ subheadline }}</p> <div class="d-flex gap-3 flex-wrap"> <a [href]="primaryHref" class="btn btn-lg px-4 fw-semibold text-white" style="background:#fd4766;border:none;">{{ primaryLabel }}</a> <a [href]="secondaryHref" class="btn btn-outline-light btn-lg px-4 fw-semibold"> {{ secondaryLabel }} </a> </div> <!-- Social proof --> <div class="d-flex gap-4 mt-4"> <div *ngFor="let s of stats; let i = index" class="d-flex gap-3 align-items-start"> <div *ngIf="i > 0" class="border-start border-secondary opacity-50 ps-4"></div> <div> <div class="fw-bold text-white fs-4">{{ s.value }}</div> <div class="text-secondary small">{{ s.label }}</div> </div> </div> </div> </div> <!-- Right: code preview --> <div class="col-lg-6 d-none d-lg-block"> <div class="rounded-3 p-4 shadow-lg" style="background:#111;border:1px solid #2d2d4e;"> <div class="d-flex gap-1 mb-3"> <span class="rounded-circle" style="width:10px;height:10px;background:#ef4444;display:block;"></span> <span class="rounded-circle" style="width:10px;height:10px;background:#f59e0b;display:block;"></span> <span class="rounded-circle" style="width:10px;height:10px;background:#10b981;display:block;"></span> </div> <pre style="color:#e2e8f0;font-size:0.8rem;margin:0;line-height:1.6;">{{ codePreview }}</pre> </div> </div> </div> </div> </section> ` }) export class SplitHeroComponent { @Input() headline = 'Build Faster with' @Input() accentLine = 'Angular 22 Templates' @Input() subheadline = 'Production-ready Angular 22 + Bootstrap 5 admin dashboards.' @Input() primaryLabel = 'Browse Templates' @Input() primaryHref = 'https://lettstartdesign.com' @Input() secondaryLabel = 'Free Components' @Input() secondaryHref = '/angular' @Input() stats = [ { value: '50+', label: 'Components' }, { value: '4.9โ ', label: 'Rating' }, ] @Input() codePreview = `@Component({ selector: 'app-dashboard', standalone: true, template: \` <app-sidebar /> <main> <app-stats-card *ngFor="let s of stats()" [data]="s" /> </main> \` }) export class DashboardComponent { stats = signal<Stat[]>([]) }` }
Animated Hero with Signals
Fade-in animation triggered after mount:
// animated-hero.component.ts import { Component, signal, OnInit } from '@angular/core' import { NgClass } from '@angular/common' @Component({ selector: 'app-animated-hero', standalone: true, imports: [NgClass], styles: [` .hero-content { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; } .hero-content.visible { opacity: 1; transform: translateY(0); } .delay-1 { transition-delay: 0.1s; } .delay-2 { transition-delay: 0.25s; } .delay-3 { transition-delay: 0.4s; } .delay-4 { transition-delay: 0.55s; } `], template: ` <section class="py-5 text-center" style="background:#0d0d0d;min-height:480px;"> <div class="container py-5"> <span class="hero-content badge px-3 py-2 mb-3 fw-semibold delay-1" [ngClass]="{ visible: visible() }" style="background:rgba(253,71,102,0.15);color:#fd4766;"> ๐ Angular 22 is here </span> <h1 class="hero-content display-4 fw-bold text-white mb-3 delay-2" [ngClass]="{ visible: visible() }"> Admin Templates for<br> <span style="color:#fd4766;">Angular 22</span> </h1> <p class="hero-content text-secondary fs-5 mx-auto mb-4 delay-3" [ngClass]="{ visible: visible() }" style="max-width:520px;"> Signals, standalone components, Bootstrap 5. Use code FIRST30 for 30% off. </p> <div class="hero-content d-flex gap-3 justify-content-center delay-4" [ngClass]="{ visible: visible() }"> <a href="https://lettstartdesign.com" class="btn btn-lg px-4 fw-semibold text-white" style="background:#fd4766;border:none;"> Browse Templates โ </a> <a href="/angular" class="btn btn-outline-light btn-lg px-4 fw-semibold"> Free Components </a> </div> </div> </section> ` }) export class AnimatedHeroComponent implements OnInit { visible = signal(false) ngOnInit() { setTimeout(() => this.visible.set(true), 100) } }
Key Classes Reference
| Class / API | Purpose |
|---|---|
position-relative / position-absolute | Layer overlay over background image |
display-4 fw-bold | Large hero headline sizing |
min-height inline style | Prevent hero from collapsing on sparse content |
d-none d-lg-block | Hide code preview panel on mobile |
signal() | Reactive visible/billing state |
@Input() config | Pass all hero data from parent |
ngClass + CSS transition | Simple fade-in without Angular animations module |
flex-wrap | CTAs stack on small screens |
Frequently Asked Questions
Create a HeroComponent with @Input() props for headline, subheadline, primaryCta, secondaryCta and backgroundImage. Use Bootstrap's display utility classes for typography, gap utilities for button spacing, and CSS custom properties or inline styles for the background image.
Use Angular's built-in @triggerAnimations with state and transition, or use a simpler approach with signals and CSS classes. Set a signal to true after a delay with setTimeout in ngOnInit, then bind a CSS animation class conditionally with [class.animate].
Apply a position-relative wrapper div with the background image, then add a child div with position-absolute inset-0 and a CSS linear-gradient background with rgba values for the overlay effect. Set z-index on the content above the overlay.
Related
Need a Full Angular + Bootstrap Admin Dashboard?
Marvel Angular Dashboard โ Angular 21 + Bootstrap 5 with 50+ components and dark mode.
Browse Templates โUse code FIRST30 for 30% off.