> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/withastro/astro/llms.txt
> Use this file to discover all available pages before exploring further.

# Components

> Learn about Astro components, props, slots, and the component script syntax

Astro components (`.astro` files) are the foundation of any Astro project. They are HTML-only templating components with no client-side runtime.

## Component Structure

An Astro component consists of two main parts: the **component script** (JavaScript/TypeScript) and the **component template** (HTML).

```astro theme={null}
---
// Component Script (optional)
// JavaScript/TypeScript code runs at build time
import Header from './Header.astro';
const greeting = 'Hello, world!';
---

<!-- Component Template -->
<!-- HTML with JSX-like expressions -->
<div>
  <Header />
  <h1>{greeting}</h1>
</div>
```

<Info>
  The component script is enclosed in **code fences** (`---`) and runs during the build. This code never reaches the browser.
</Info>

## Component Script

The component script runs at build time on the server. You can:

* Import other components
* Import data from other files
* Fetch data from APIs or databases
* Create variables to use in your template

```astro title="src/components/BlogPost.astro" theme={null}
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { getEntry } from 'astro:content';

// Fetch data at build time
const post = await getEntry('blog', Astro.params.slug);
const { Content } = await post.render();

// Process data
const readingTime = calculateReadingTime(post.body);
const publishedDate = new Date(post.data.pubDate).toLocaleDateString();
---

<BaseLayout title={post.data.title}>
  <article>
    <h1>{post.data.title}</h1>
    <p>Published: {publishedDate} • {readingTime} min read</p>
    <Content />
  </article>
</BaseLayout>
```

<Note>
  Everything in the component script runs **once at build time**, not on every request.
</Note>

## Props

Components accept data through props, similar to React or Vue:

```astro title="src/components/Card.astro" theme={null}
---
const { title, description, href } = Astro.props;
---

<article class="card">
  <h3>{title}</h3>
  <p>{description}</p>
  <a href={href}>Read more</a>
</article>
```

Use it:

```astro theme={null}
---
import Card from './components/Card.astro';
---

<Card 
  title="First Post" 
  description="This is my first blog post"
  href="/blog/first-post"
/>
```

### TypeScript Props

Define prop types with TypeScript:

```astro title="src/components/Card.astro" theme={null}
---
interface Props {
  title: string;
  description: string;
  href: string;
  image?: string;
}

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

<article class="card">
  {image && <img src={image} alt={title} />}
  <h3>{title}</h3>
  <p>{description}</p>
  <a href={href}>Read more</a>
</article>
```

<Tabs>
  <Tab title="Basic Props">
    ```astro theme={null}
    ---
    const { name, age } = Astro.props;
    ---
    <p>{name} is {age} years old</p>
    ```
  </Tab>

  <Tab title="Typed Props">
    ```astro theme={null}
    ---
    interface Props {
      name: string;
      age: number;
    }
    const { name, age } = Astro.props;
    ---
    <p>{name} is {age} years old</p>
    ```
  </Tab>

  <Tab title="Default Values">
    ```astro theme={null}
    ---
    interface Props {
      name: string;
      role?: string;
    }
    const { name, role = 'Guest' } = Astro.props;
    ---
    <p>{name} - {role}</p>
    ```
  </Tab>
</Tabs>

## Slots

Slots allow you to pass child content to components:

```astro title="src/components/Card.astro" theme={null}
---
const { title } = Astro.props;
---

<div class="card">
  <h2>{title}</h2>
  <div class="content">
    <slot />
  </div>
</div>
```

Use it:

```astro theme={null}
<Card title="My Card">
  <p>This content goes in the slot!</p>
  <button>Click me</button>
</Card>
```

### Named Slots

Define multiple slots with names:

```astro title="src/components/BlogPost.astro" theme={null}
<article>
  <header>
    <slot name="header" />
  </header>
  
  <main>
    <slot />  <!-- default slot -->
  </main>
  
  <footer>
    <slot name="footer" />
  </footer>
</article>
```

Use named slots:

```astro theme={null}
<BlogPost>
  <h1 slot="header">My Blog Post</h1>
  
  <p>This goes in the default slot.</p>
  <p>Multiple elements work too!</p>
  
  <p slot="footer">Written by Alice</p>
</BlogPost>
```

### Fallback Content

Provide default content if no slot content is passed:

```astro theme={null}
<div class="card">
  <slot>
    <p>No content provided</p>
  </slot>
</div>
```

### Slot Implementation

From the source code at `src/runtime/server/render/slot.ts`, slots are rendered as functions that return template results:

```ts theme={null}
export function renderSlot(
  result: SSRResult,
  slotted: ComponentSlotValue | RenderTemplateResult,
  fallback?: ComponentSlotValue | RenderTemplateResult,
): RenderInstance {
  if (!slotted && fallback) {
    return renderSlot(result, fallback);
  }
  return {
    async render(destination) {
      await renderChild(
        destination, 
        typeof slotted === 'function' ? slotted(result) : slotted
      );
    },
  };
}
```

## The Astro Global

Every Astro component has access to the global `Astro` object:

```astro theme={null}
---
// Route parameters from dynamic routes
const { id } = Astro.params;

// Component props
const { title } = Astro.props;

// Request information
const url = Astro.url;
const headers = Astro.request.headers;
---
```

<Tabs>
  <Tab title="Common Properties">
    ```ts theme={null}
    Astro.props        // Component props
    Astro.params       // Route parameters
    Astro.request      // Request object
    Astro.url          // Request URL
    Astro.site         // Site URL from config
    Astro.slots        // Slot utilities
    ```
  </Tab>

  <Tab title="Utilities">
    ```ts theme={null}
    Astro.redirect()   // Redirect to another page
    Astro.slots.has()  // Check if slot has content
    Astro.cookies      // Cookie helpers
    ```
  </Tab>
</Tabs>

## Framework Components

Astro components can render framework components from React, Vue, Svelte, and more:

```astro theme={null}
---
import ReactCounter from './ReactCounter.jsx';
import VueButton from './VueButton.vue';
import SvelteCard from './SvelteCard.svelte';
---

<div>
  <!-- By default, these render as static HTML -->
  <ReactCounter />
  <VueButton />
  
  <!-- Add client directive for interactivity -->
  <SvelteCard client:load />
</div>
```

<Warning>
  Framework components are static by default. Add a [client directive](/concepts/islands) to make them interactive.
</Warning>

## Component Rendering

From `src/runtime/server/render/component.ts`, Astro determines how to render each component:

```ts theme={null}
export function renderComponent(
  result: SSRResult,
  displayName: string,
  Component: unknown,
  props: Record<string | number, any>,
  slots: ComponentSlots = {},
): RenderInstance | Promise<RenderInstance> {
  // Astro components
  if (isAstroComponentFactory(Component)) {
    return renderAstroComponent(result, displayName, Component, props, slots);
  }
  
  // Framework components (React, Vue, etc.)
  return renderFrameworkComponent(result, displayName, Component, props, slots);
}
```

## Practical Examples

### Reusable Button Component

```astro title="src/components/Button.astro" theme={null}
---
interface Props {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  href?: string;
}

const { variant = 'primary', size = 'md', href } = Astro.props;
const Tag = href ? 'a' : 'button';
---

<Tag 
  class={`btn btn-${variant} btn-${size}`}
  href={href}
>
  <slot />
</Tag>

<style>
  .btn {
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
    font-weight: 600;
  }
  
  .btn-primary {
    background: #3b82f6;
    color: white;
  }
  
  .btn-sm { font-size: 0.875rem; }
  .btn-md { font-size: 1rem; }
  .btn-lg { font-size: 1.125rem; }
</style>
```

### Data-Fetching Component

```astro title="src/components/UserProfile.astro" theme={null}
---
interface Props {
  userId: string;
}

const { userId } = Astro.props;

// Fetch data at build time
const response = await fetch(`https://api.example.com/users/${userId}`);
const user = await response.json();
---

<div class="profile">
  <img src={user.avatar} alt={user.name} />
  <h2>{user.name}</h2>
  <p>{user.bio}</p>
  <a href={`mailto:${user.email}`}>Contact</a>
</div>
```

### Conditional Rendering

```astro title="src/components/Alert.astro" theme={null}
---
interface Props {
  type: 'info' | 'warning' | 'error' | 'success';
  title?: string;
}

const { type, title } = Astro.props;

const icons = {
  info: '💡',
  warning: '⚠️',
  error: '❌',
  success: '✅',
};
---

<div class={`alert alert-${type}`}>
  <span class="icon">{icons[type]}</span>
  <div class="content">
    {title && <h4>{title}</h4>}
    <slot />
  </div>
</div>
```

## Best Practices

<Steps>
  <Step title="Keep components focused">
    Each component should have a single responsibility.
  </Step>

  <Step title="Use TypeScript">
    Type your props for better developer experience and fewer bugs.
  </Step>

  <Step title="Extract reusable logic">
    Move complex logic to utility functions in the component script.
  </Step>

  <Step title="Use slots for flexibility">
    Slots make components more reusable and composable.
  </Step>

  <Step title="Default to Astro components">
    Only use framework components when you need client-side interactivity.
  </Step>
</Steps>

## Learn More

<CardGroup cols={2}>
  <Card title="Islands Architecture" href="/concepts/islands">
    Add interactivity with client directives
  </Card>

  <Card title="Layouts" href="/concepts/layouts">
    Create reusable page structures
  </Card>
</CardGroup>
