Day7 - Astro Series: Build a button component

A pretty gradient background with a heading: "Implementing a Button Component"

Introduction

Previously we learned how to write components and add styles, giving us the basics for building components. In the next two chapters we’ll keep the momentum and create a practical and highly representative component — the “Button”.

Defining the problem

Websites require many types of buttons, but creating a separate component for each is hard to manage — for example: <a> buttons, <button> buttons… Button styles also vary widely: solid, outline, ghost, underline link, info, warning, danger, success… Writing a component for each button type is exhausting! So the goal is to use what we learned to write a “universal button component” and create specific button variants by passing props — the “Button.astro”.

Step 1: Define Props

First, the button will accept custom props like link, theme, and size. Use the rest parameters to destructure any remaining props and spread them onto the element:

---
const {href, theme, size, ...rest } = Astro.props
---
<a href={href} {...rest}>
<slot/>
</a>

Step 2: Choose the element dynamically

Not every button has a link, so choose the tag dynamically: if href exists use an <a> tag, otherwise use a <button> tag:

---
const {href, theme, size, ...rest } = Astro.props
const Element = href ? 'a' : 'button'
---
<Element href={href} {...rest}>
<slot/>
</Element>

Step 3: Add button styles

By default apply the btn class and determine style and size classes based on the passed props:

---
const {href, theme, size, ...rest } = Astro.props
const Element = href ? 'a' : 'button'
const themeClass = theme ? `btn--${theme}` : 'btn--primary'
const sizeClass = size ? `btn--${size}` : 'btn--md'
---
<Element href={href} class:list={['btn', themeClass, sizeClass]} {...rest}>
<slot/>
</Element>

Then write the corresponding button style rules:

<style>
.btn {}
.btn:hover {}
.btn--primary {}
.btn--secondary {}
.btn--sm {}
.btn--md {}
.btn--lg {}
</style>

With that, the Button.astro super button is complete! Provide the appropriate props and slot, and the component will render the corresponding button variant.

Extra step: Add types

Finally, add TypeScript for the component props.

interface Props {
href?: string;
size?: "sm" | "md" | "lg";
theme?:
| "primary"
| "secondary"
}

As for typing the element/tag, I think it relates to the Polymorphic type🔗, but I don’t yet understand how that helper works 😅. If you know, feel free to share.

Summary

This chapter combined the earlier “basic components” and “styles” sections to build a reusable web Button component. Try implementing it yourself. Buttons are one of the most representative UI elements on the web; the next chapter will cover integrating Tailwind using this example — stay tuned!

Further reading