Custom Components

Custom layouts compose from section components located in src/layouts/custom/components/. These components are designed for landing pages and marketing content.

Component Directory

src/layouts/custom/components/
├── hero/
│   └── default/
│       ├── Hero.astro         # Hero section
│       └── styles.css
│
├── features/
│   └── default/
│       ├── Features.astro     # Feature grid
│       └── styles.css
│
└── content/
    └── default/
        ├── Content.astro      # Simple content wrapper
        └── styles.css

Hero

The Hero component renders a prominent header section with title, subtitle, and call-to-action buttons.

File: src/layouts/custom/components/hero/default/Hero.astro

Props

interface Props {
  hero: {
    title: string;
    subtitle?: string;
    cta?: {
      label: string;
      href: string;
    };
    secondaryCta?: {
      label: string;
      href: string;
    };
  };
}

Usage

---
import Hero from '../../components/hero/default/Hero.astro';

const hero = {
  title: "Build Beautiful Docs",
  subtitle: "A modern documentation framework",
  cta: {
    label: "Get Started",
    href: "/docs"
  }
};
---

<Hero hero={hero} />

Structure

---
interface Props {
  hero: {
    title: string;
    subtitle?: string;
    cta?: { label: string; href: string };
    secondaryCta?: { label: string; href: string };
  };
}

const { hero } = Astro.props;
---

<section class="hero">
  <div class="hero__content">
    <h1 class="hero__title">{hero.title}</h1>

    {hero.subtitle && (
      <p class="hero__subtitle">{hero.subtitle}</p>
    )}

    <div class="hero__actions">
      {hero.cta && (
        <a href={hero.cta.href} class="hero__cta hero__cta--primary">
          {hero.cta.label}
        </a>
      )}

      {hero.secondaryCta && (
        <a href={hero.secondaryCta.href} class="hero__cta hero__cta--secondary">
          {hero.secondaryCta.label}
        </a>
      )}
    </div>
  </div>
</section>

Styling

.hero {
  padding: 6rem 2rem;
  text-align: center;
  background: linear-gradient(
    to bottom,
    var(--color-bg-secondary),
    var(--color-bg-primary)
  );
}

.hero__title {
  font-size: 3rem;
  font-weight: 700;
  margin-bottom: 1rem;
}

.hero__subtitle {
  font-size: 1.25rem;
  color: var(--color-text-secondary);
  max-width: 600px;
  margin: 0 auto 2rem;
}

.hero__actions {
  display: flex;
  gap: 1rem;
  justify-content: center;
}

.hero__cta {
  padding: 0.75rem 1.5rem;
  border-radius: 8px;
  font-weight: 500;
  text-decoration: none;
}

.hero__cta--primary {
  background: var(--color-primary);
  color: white;
}

.hero__cta--secondary {
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-text-primary);
}

Features

The Features component renders a grid of feature cards.

File: src/layouts/custom/components/features/default/Features.astro

Props

interface Props {
  features: Array<{
    icon?: string;
    title: string;
    description: string;
  }>;
}

Usage

---
import Features from '../../components/features/default/Features.astro';

const features = [
  {
    icon: "🚀",
    title: "Fast",
    description: "Built on Astro for lightning-fast builds"
  },
  {
    icon: "🧩",
    title: "Modular",
    description: "Pick and choose layouts"
  },
  {
    icon: "⚡",
    title: "Simple",
    description: "YAML configuration"
  }
];
---

<Features features={features} />

Structure

---
interface Props {
  features: Array<{
    icon?: string;
    title: string;
    description: string;
  }>;
}

const { features } = Astro.props;
---

<section class="features">
  <div class="features__grid">
    {features.map(feature => (
      <div class="feature-card">
        {feature.icon && (
          <span class="feature-card__icon">{feature.icon}</span>
        )}
        <h3 class="feature-card__title">{feature.title}</h3>
        <p class="feature-card__description">{feature.description}</p>
      </div>
    ))}
  </div>
</section>

Styling

.features {
  padding: 4rem 2rem;
  max-width: 1200px;
  margin: 0 auto;
}

.features__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 2rem;
}

.feature-card {
  padding: 2rem;
  background: var(--color-bg-secondary);
  border-radius: 12px;
  text-align: center;
}

.feature-card__icon {
  font-size: 2.5rem;
  display: block;
  margin-bottom: 1rem;
}

.feature-card__title {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.feature-card__description {
  color: var(--color-text-secondary);
  line-height: 1.6;
}

Content

The Content component is a simple wrapper for text content.

File: src/layouts/custom/components/content/default/Content.astro

Props

interface Props {
  title?: string;
  description?: string;
}

Usage

---
import Content from '../../components/content/default/Content.astro';
---

<Content title="About Us" description="Our story and mission">
  <p>Additional content can go here via slot.</p>
</Content>

Structure

---
interface Props {
  title?: string;
  description?: string;
}

const { title, description } = Astro.props;
---

<section class="content-section">
  {title && <h1 class="content-section__title">{title}</h1>}

  {description && (
    <div class="content-section__body">
      <p>{description}</p>
    </div>
  )}

  <slot />
</section>

Styling

.content-section {
  max-width: 720px;
  margin: 0 auto;
  padding: 4rem 2rem;
}

.content-section__title {
  font-size: 2.5rem;
  font-weight: 700;
  margin-bottom: 1.5rem;
}

.content-section__body {
  font-size: 1.125rem;
  line-height: 1.75;
  color: var(--color-text-secondary);
}

.content-section__body p {
  margin-bottom: 1.5rem;
}

Creating Custom Components

Example: Testimonials Component

mkdir -p src/layouts/custom/components/testimonials/default/
---
// testimonials/default/Testimonials.astro
interface Props {
  testimonials: Array<{
    quote: string;
    author: string;
    role?: string;
    avatar?: string;
  }>;
}

const { testimonials } = Astro.props;
---

<section class="testimonials">
  <h2>What people are saying</h2>

  <div class="testimonials__grid">
    {testimonials.map(t => (
      <blockquote class="testimonial">
        <p class="testimonial__quote">"{t.quote}"</p>
        <footer class="testimonial__author">
          {t.avatar && <img src={t.avatar} alt="" />}
          <div>
            <cite>{t.author}</cite>
            {t.role && <span>{t.role}</span>}
          </div>
        </footer>
      </blockquote>
    ))}
  </div>
</section>

<style>
  .testimonials {
    padding: 4rem 2rem;
    background: var(--color-bg-secondary);
  }

  .testimonials__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2rem;
    max-width: 1200px;
    margin: 0 auto;
  }

  .testimonial {
    background: var(--color-bg-primary);
    padding: 2rem;
    border-radius: 12px;
  }
</style>

Using in Layout

---
// home/Layout.astro
import Hero from '../../components/hero/default/Hero.astro';
import Features from '../../components/features/default/Features.astro';
import Testimonials from '../../components/testimonials/default/Testimonials.astro';

// ... load data ...
---

<div class="home">
  {hero && <Hero hero={hero} />}
  {features && <Features features={features} />}
  {testimonials && <Testimonials testimonials={testimonials} />}
</div>

Component Variants

Create variants for different visual styles:

components/hero/
├── default/          # Standard centered hero
├── split/            # Image on one side
└── video/            # Background video

Use in layouts:

---
// Use split variant
import Hero from '../../components/hero/split/Hero.astro';
---

Style Imports

Import component styles in your layout:

---
import Hero from '../../components/hero/default/Hero.astro';
import Features from '../../components/features/default/Features.astro';

// Import styles
import '../../components/hero/default/styles.css';
import '../../components/features/default/styles.css';
---

Or create a central import file:

/* home/styles.css */
@import '../../components/hero/default/styles.css';
@import '../../components/features/default/styles.css';
---
import './styles.css';
---