5 Tips for Sharing code between NuxtJS Projects
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.