Introduction
Previously, besides creating a new project, we also looked at the general appearance of the Astro CLI and configuration. In this chapter we’ll start by creating basic Astro components. Components can be placed inside the previously mentioned src folder; it’s recommended to create a components folder inside for easier management.
Creating Components
You can write an Astro component by creating a file with the .astro extension. A component is split into a script and a template section, separated by fences (---).
---// Component script---<!-- Component template -->The component script represents how the component will run on the server. Here you can write JavaScript or TypeScript to:
- Import other Astro components
- Import components from different frameworks
- Import and fetch data
- Receive data passed to the component (props)
- Create variables and access them in the component template
---// 1. Import Astro componentsimport SomeAstroComponent from './components/SomeAstroComponent.astro';
// 2. Import components from different frameworksimport SomeReactComponent from './components/SomeReactComponent.jsx';
// 3. Import dataimport pokemons from '../data/pokemon.json';
// 3. Fetch dataconst user = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
// 4. Use destructuring to get props passed to the componentconst { title } = Astro.props;const message = 'Astro is Awesome!'---
<!-- 5. Create variables and access them in the component template -->I think: { message }{ title }Regarding point 5, you can create variables in server-side JavaScript and insert them into the template using {} curly braces, whether they are attributes or passed in as props:
---import Modal from './components/Modal.astro'const message = 'Hello Astro'---<Modal class={message} message={`${message} Here Is My Props`} />Or write JavaScript expressions to generate HTML, similar to JSX:
---const fruits = ['Apple', 'Banana', 'Cherry']---
<ul> {fruits.map(fruit => <li>{fruit}<li>)}</ul>Or conditionally render template content:
---const isOnSale = true---
{isOnSale && <p>On sale now!</p>}{isOnSale ? <p>On sale now!</p> : <p>No sale at the moment</p>}Or dynamically decide the tag type (note that dynamic tags do not support hydration, and tag names must be capitalized to distinguish between native tags and custom components):
---import MyComponent from "./MyComponent.astro";const Element = 'div'const Component = MyComponent;---
<Element>Hello!</Element> <!-- Renders as <div>Hello!</div> --><Component /> <!-- Renders as <MyComponent /> -->Component Props
You can set components to accept externally passed properties by accessing Astro.props in the server-side script. In the example below, the two variables greeting and name are created via destructuring:
---// Usage from outside: <GreetingHeadline greeting="Hi" name="Joe" />const { greeting, name } = Astro.props;---<h2>{greeting}, {name}!</h2>You can even add types for props so your editor knows what type of values should be passed to the component:
interface Props { greeting: string; name: string;}Component Slots
In addition to props, you can pass external HTML content into components via slots. For example, since most site pages include a navigation bar and footer component, you can create a Base component to serve as a common layout.
---import Navbar from '../components/Navbar.astro'import Hero from '../components/Hero.astro'import Footer from '../components/Footer.astro'---<Navbar /> <Hero /> <slot /><Footer />Then import that component in each page. This not only centralizes the page structure but also avoids repeatedly importing basic components on every page.
---import Base from '../layouts/Base.astro---<Base> <!-- The template in this area will be injected into the slot --></Base>Named Slots
You can have more than one slot; use named slots to specify which slot you want to inject content into. Based on the earlier example:
<Navbar /> <slot name="before-hero" /> <Hero /> <slot /><Footer />You can use the slot attribute to specify the name of the slot you want to inject into.
<Base> <!-- The image below will be injected into the before-hero slot --> <img src ="example.jpg" slot="before-hero"> <!-- The following, without a specific slot, will be injected into the default slot --> <h1>Hello World</h1></Base>Slot Fallbacks
When defining the location of a slot, you can add content that will serve as the default when nothing is passed into the slot.
<slot> <p>This is the default fallback content used when no template is passed into the slot</p></slot>Slot Forwarding
Slots can be forwarded into other components. For example, there is a component BaseLayout.astro:
------<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <slot name="head"/> </head> <body> <slot /> </body></html>And it is referenced by HomeLayout.astro:
---import BaseLayout from "./BaseLayout.astro";---
<BaseLayout> <slot name="head" slot="head"/> <slot /></BaseLayout>At this point, passing a head slot to HomeLayout.astro will forward it into the head slot of BaseLayout.
---import HomeLayout from "../layouts/HomeLayout.astro";---<HomeLayout> <title slot="head">Astro</title> <h1>Astro</h1></HomeLayout>Summary
If you’ve used other frameworks or JSX before, you’ll find the concept of “components” familiar and easy to pick up. Since .astro is primarily designed for server rendering, you don’t have to worry about reactive state complexity, which greatly reduces complexity!
Finally, it’s recommended to practice hands-on. If you encounter issues, you can check out my example:
- Create common components like
NavbarandFooterinside thecomponentsfolder. - Import and display your new components in pages under the
pagesfolder. - Create a
Basecomponent inside thelayoutsfolder and use<slot />so all site pages use that component — you can even accept props to make the layout more flexible (for example, accepting changes to<head>meta tags). - Write appropriate TypeScript for type definitions.
Further Reading
- Components - Astro DOCS
- Component Props - Astro DOCS
- Day5 - 基礎元件 - The same article also published on iThome Ironman Challenge