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 / APIPurpose
position-relative / position-absoluteLayer overlay over background image
display-4 fw-boldLarge hero headline sizing
min-height inline stylePrevent hero from collapsing on sparse content
d-none d-lg-blockHide code preview panel on mobile
signal()Reactive visible/billing state
@Input() configPass all hero data from parent
ngClass + CSS transitionSimple fade-in without Angular animations module
flex-wrapCTAs 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.