5 Tips for Sharing code between NuxtJS Projects

Michael Gallagher
DailyJS
Published in
4 min readSep 8, 2020

Many companies have multiple portals, and having them share common code is certainly the scenario we’d prefer, so what are the challenges to sharing code between Nuxt projects?

Normally shared code is in the form of an npm package with importable components, and that can be done with Nuxt, but the thing about Nuxt is that it offers some lovely features that are exclusively tied to its directory structure, and it only allows for one such structure.

What if we want to share pages, layouts or components between projects? Let’s setup a sample project and walk through some ways to do that.

1) Lerna

While not necessary, a lerna mono repo is easy to setup and means a great developer experience. We can make modifications in common code while running a portal and get hot reloading.

Within a new folder let’s create a 2 package repo:

> npm install -g lerna
> lerna init
> lerna create portal-one --dependencies nuxt --yes
> lerna create portal-two --dependencies nuxt portal-one --yes
> lerna bootstrap

The empty packages can be started like this:

> cd packages/portal-one
> npx nuxt

Now the dev server is running and showing the default page.

2) Pages

In the first package we are going to create the first page.

> mkdir pages
> cd pages
> echo '<template><div>Simple page</div></template' > one.vue

Now view http://localhost:3000/one (note the root page will no longer be generated)

In order to get this page to be included in portal-two, we will package it as a Nuxt module. So in the root of portal-one create a file called module.js with the following:

// Nuxt exposes its default route builder logic here
import { createRoutes } from '@nuxt/utils'
// with a lot of pages it might be worth considering a folder pass
// to dynamically create this list

const pages = ['pages/one.vue']
export default function NuxtModule() {
const { routeNameSplitter, trailingSlash } = this.options.router
this.extendRoutes((routes) => {
routes.push(...createRoutes({
files: pages,
srcDir: __dirname,
pagesDir: 'pages',
routeNameSplitter,
trailingSlash,
}))
})
}

Nuxt modules have a convenient extendRoutes helper exposed, coupling this with the Nuxt page-to-route generation (createRoutes), the code to inject the page routes is short and straight-forward.

In portal-two add a nuxt.config.js file with:

export default {
modules: ['portal-one/module']
}

This could be configured as a build only module, but we might need more options later.

Now stop the portal-one nuxt server and start one in portal-two(npx nuxt).

View http://localhost:3000/one to confirm the page route was added successfully.

3) Layouts

Back to portal-one and we will create a default layout:

> mkdir layouts
> cd layouts
> echo '<template><center><nuxt/></center></template>' > default.vue

In module.js add :

import { createRoutes, relativeTo } from '@nuxt/utils'
import path from 'path'
...// at the end of the NuxtModule functionconst layoutPath = (file) =>
relativeTo(
this.options.buildDir,
path.resolve(__dirname, 'layouts', file),
)

this.nuxt.hook('build:templates', ({ templateVars }) => {
templateVars.layouts.default = layoutPath('default.vue')
})

Here we are using a hook to provide layouts at the right time in the build process. The layoutPath function will provide a path to the dependency layout files relative to the project build directory.

Refresh http://localhost:3000/one and the new default layout should be applied.

4) Static content

In portal-one, we create a static robots file:

> mkdir static
> cd static
> echo 'User-agent: *' > robots.txt

In module.js add :

import serveStatic from 'serve-static'...// at the end of the NuxtModule functionthis.addServerMiddleware(
serveStatic(path.resolve(__dirname, 'static')),
)

This is the same middleware Nuxt uses internally for a local static folder, we just configure an additional one connected to the module’s static content.

Restart the portal-two Nuxt server and view http://localhost:3000/robots.txt

5) Components

As already mentioned, components can easily be imported from an npm package, but there is a nice feature in recent Nuxt versions, which discovers components. There are extension patterns out of the box, but here is another.

First let’s create our component and a page in portal-one:

> mkdir components
> cd components
> echo '<template><div>Simple Component</div></template>' > comp.vue
> cd ../pages
> echo '<template><comp/></template>' > compPage.vue

and if you want to test it in portal-one(from package root):

> echo 'export default { components: true }' > nuxt.config.js
> npx nuxt

View http://localhost:3000/compPage.

Now to provide the same component and page in the module.js:

// update pages array
const pages = ['pages/one.vue', 'pages/compPage.vue']
....// at the end of the NuxtModule functionthis.nuxt.hook('components:dirs', (dirs) => {
dirs.unshift({
path: path.resolve(__dirname, 'components'),
level: 1, // provide a priority
})
})

This is a hook specifically provided to extend the folders for components, however most examples specify a prefix and I have intentionally left it out.

Back in portal-two, add the components: true to the nuxt config, and run the server. http://localhost:3000/compPage should work here too.

Now from portal-two:

> mkdir components
> cd components

Create comp.vue and add:

<template>
<comp class="overridden"/>
</template>
<style>
.overridden { text-transform: uppercase; }
</style>
<script>
import comp from 'portal-one/components/comp.vue'
export default {
components: { comp },
}
</script>

Back to http://localhost:3000/compPage and the local override should be applied. This pattern allows the local project to override common components (as a proxy if required) and provide them not only locally, but in common code also (the compPage in this case).

That wraps it up, hope you find them helpful.

Update: Credit to Thomas Nohl for providing a simpler and more reliable solution for controlling component priority when providing common components.

--

--

Michael Gallagher
DailyJS

Living in the vibrant city of Buenos Aires, developing eCommerce software with Hip. Modern JS is my passion, Vue my tool of choice