On the fourth day of Enhancing: Composing elements

Simon MacDonald’s avatar

by Simon MacDonald
@macdonst
@macdonst@mastodon.online
on

four birds Original photo by Anissa Terry on Unsplash

Building web components out of smaller components is one of the most powerful features of web components. Today’s lesson will show you how to build a page wrapper component to include a header and footer without having to repeat yourself.

We’ll need a header and footer to compose our page wrapper component. Run these commands in your terminal.

begin gen element --name my-header
begin gen element --name my-footer
begin gen element --name page-wrapper

This updates your project structure to include the three new elements.

12-days
├── app
│   ├── api
│   │   └── about.mjs
│   ├── elements
│   │   ├─– my-footer.mjs
│   │   ├─– my-header.mjs
│   │   ├─– page-wrapper.mjs
│   │   └── social-links.mjs
│   └── pages
│       ├─– about.html
│       └── index.html
└── public

We’ll add a navigation section for the header with links to the home page and the about page. Replace the contents of app/elements/my-header.mjs with the following code:

export default function Element ({ html }) {
 return html`
   <header class="p0">
     <h1>Header</h1>
     <nav>
       <a href="/">home</a>
       <a href="/about">about</a>
     </nav>
   </header>
 `
}

For the footer, we’ll do something a little different. We’ll use the footer as a quick way to debug our application state. I’ve found this to be very useful in practice as the structure of the JSON I expect to get back from my API occasionally differs from the JSON’s actual structure.

Replace the contents of app/elements/my-footer.mjs with:

export default function Element ({ html, state }) {
 return html`
<footer>
 <pre>${JSON.stringify(state, null, 2)}</pre>
</footer>`
}

Now that we have some child components, we can move on to the next step.

Composing components

Our page-wrapper component will be composed of our my-header and my-footer components. In addition we’ll use slots to enable passing in an arbitrary number of children to our new component. First, the code for app/elements/page-wrapper.mjs:

export default function Element ({ html }) {
 return html`
<my-header></my-header>
 <slot></slot>
<my-footer></my-footer>
`
}

It’s a pretty simple but powerful layout component. Anytime we use the page-wrapper component, it will wrap the children we pass into it with our previously developed header and footer components. It’s able to do this via the slot tag. In this case, we are using an unnamed slot. This means anything between the page-wrapper tags will be inserted where the slot tag exists.

In our app/pages/about.html page the page-wrapper component can be used like:

<page-wrapper>
 <section class="m-auto p2 font-sans">
   <h1 class="mb1 font-semibold text3">About Me</h1>
   <social-links></social-links>`
 </section>
</page-wrapper>

Which will produce the following output (see http://localhost:3333/about):

header and footer

Next Steps

Things are progressing nicely, but that page looks ugly. Stop by tomorrow, where we’ll apply some styling to our page using Enhance Styles.