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.propsconst 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
- Day7 - 實作按鈕元件 - The same article published on iThome Ironman Contest