At work we've got a component library built in Vue.js. It's great because we can take the components our designers create, build them once, and then ship them to any project.
Most, but not all, of our projects use Nuxt.js. We like it because it gives us the ability to build sites with server-side rendering, static site generation, or single-page applications as needed.
Building the components for Nuxt
We have navigational components in our library like a Navbar
component and a Footer
component. Since these components typically direct users around internal pages on our Nuxt projects, we want to use the nuxt-link
component to get the most out of the framework.
nuxt-link allows users to navigate the application like they might expect with a router-link
. It is itself an extension of router-link.
But to use nuxt-link
, the component needs to be used inside a Nuxt project. In Nuxt projects that's fine, but what do we do when we aren't using Nuxt? In those cases, we may want to use a regular HTML a
tag.
Wrapping nuxt-link
So we built a utility component to wrap our links. It's aptly named AnchorLinkOrNuxtLink
. Here's what that looks like:
<template>
<nuxt-link v-if="nuxt" :to="to">
<slot></slot>
</nuxt-link>
<a v-else :href="to">
<slot></slot>
</a>
</template>
<script>
export default {
props: ['nuxt', 'to']
}
</script>
We pass two props to the AnchorLinkOrNuxtLink
component:
-
nuxt
: a boolean value which makes the component act as anuxt-link
or ana
tag. -
to
: some string that acts as theto
prop on anuxt-link
, or thehref
attribute on ana
tag.
We use conditional rendering to check if nuxt
is true
. If so, we use nuxt-link
. Otherwise, the component renders as an a
tag. If the nuxt
prop isn't passed in, the expression will evaluate to false
and we default to the safe fallback of an a
tag, which will work in either a Nuxt project or something else.
Finally, since both nuxt-link
s and a
tags are able to wrap things, we provide a slot component inside either to contain any wrapped content.
Putting it together
We want to be able to create a flexible component that can take links and render out either nuxt-link
s or a
tags. Let's look at a quick example. Say we want a Navbar
component with three links:
- The brand element that navigates to
/
- A secondary
About
page - Some link to an external resource like
Partner site
.
We can build that like this:
<template>
<nav>
<ul>
<li>
<AnchorLinkOrNuxtLink to="/" :nuxt="brandIsNuxtLink">
<img src="some-logo.jpg" />
</AnchorLinkOrNuxtLink>
</li>
<li v-for="item in navbar.links" :key="item.link">
<AnchorLinkOrNuxtLink :to="item.link" :nuxt="item.nuxt">
{{ item.title }}
</AnchorLinkOrNuxtLink>
</li>
</ul>
</nav>
</template>
<script>
import AnchorLinkOrNuxtLink from './AnchorLinkOrNuxtLink.vue';
export default {
components: {
AnchorLinkOrNuxtLink
},
props: ["brandIsNuxtLink", "navbar"]
}
</script>
This component takes two props:
-
brandIsNuxtLink
: since the brand element is a bit different than the rest of the links in the nav, we call this out separately. We can pass a boolean to determinenuxt-link
vsa
tag behavior. -
navbar
: we can pass an object as this prop to set up the links. It might look something like this:
navbar: {
links: [
{
link: '/about',
nuxt: true,
title: 'About'
},
{
link: 'https://www.partner.com',
nuxt: false,
title: 'Partner site'
}
]
}
The /about
link will act as a Nuxt link, and the https://www.partner.com
link will act like a normal anchor link.
Fun!
Top comments (2)
Maybe I'm wrong, but couldn't you skip all the 'nuxt' prop passing if you just:
a) check for this.$nuxt and
b) check for a starting slash on the 'to' prop?
i.e.
Oh yeah! That'll do it. I appreciate the feedback and I'll probably go ahead and use this. Thanks!