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 a nuxt-link or an a tag.
  • to: some string that acts as the to prop on a nuxt-link, or the href attribute on an a 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-links 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-links or a tags. Let's look at a quick example. Say we want a Navbar component with three links:

  1. The brand element that navigates to /
  2. A secondary About page
  3. 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">
                    
                </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 determine nuxt-link vs a 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!