How to implement Nuxt-like file-based routing in Vue?


Vue.js, a rapidly growing progressive JavaScript framework, began with a single person's vision and has attracted thousands of developers. In this article, we will explore file-based vue routing and how we can enhance it to the next level.
What is vue-router?
Vue Router, the official router for Vue.js, deeply integrates with Vue.js core, making it simple to build single-page applications.
Furthermore, the Vue Router is used for client-side routing in Vue SPAs. It links the URL to the content displayed and doesn't require page reloads as users navigate the application.
When using the command below to create a Vue.js project, we can choose to integrate vue-router into the project by selecting an option.
npm create vue@latest
If you want to implement vue-router in an existing Vue.js project, follow these steps:
To proceed with our project, we must install the 'vue-router' package.
npm install vue-router@4
Now, let's create a router/index.ts
file to configure vue-router. If your project is not typescript-based, create a index.js
file.
Now put the below code in the router/index.ts
file.
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'
const routes = [
{ path: '/', name:'home', component: HomeView },
{ path: '/about', name:'about', component: AboutView },
]
const router = createRouter({
history: createMemoryHistory(),
routes,
})
export default router
In the above code, we have configured vue-router and created two routes named home and about. These two routes are linked to our two SFC components HomeView.vue
and AboutView.vue
. In this file, we have also exported the router instance as well.
Now, let's initialize the vue-router in our main.ts file.
import { createApp } from 'vue'
import router from './router'
import App from './app.vue'
createApp(App)
.use(router)
.mount('#app')
After adding vue-router to our project, we can use <RouterView />
components to render every route component in our app.vue
file. Refer to the below code.
<template>
<h1>Hello App!</h1>
<p>
<strong>Current route path:</strong> {{ $route.fullPath }}
</p>
<nav>
<RouterLink to="/">Go to Home</RouterLink>
<RouterLink to="/about">Go to About</RouterLink>
</nav>
<main>
<RouterView />
</main>
</template>
After following the above steps, we have successfully added the vue-router to our project. From now on, we can add all our routes to the router/index.ts
file.
What problem do we face while using the standard vue-router approach?
One major problem with the current approach is that we have to manually maintain the routes in the router/index.ts
file. If we add or remove any pages, we must add or remove the corresponding route declaration from these files.
Nuxt.js has fixed the problem. You don't need to maintain a separate routes file anymore because nuxt.js provides a zero-config file-based routing system.
What is the Nuxt.js file-system routing?
Nuxt routing is based on vue-router and generates the routes from every component created in the pages/
directory, based on their filename. This file system routing uses naming conventions to create dynamic and nested routes.
Directory structure:
| pages/
---| about.vue
---| index.vue
---| posts/
-----| [id].vue
Generated routes based on directory structure
{
"routes": [
{
"path": "/about",
"component": "pages/about.vue"
},
{
"path": "/",
"component": "pages/index.vue"
},
{
"path": "/posts/:id",
"component": "pages/posts/[id].vue"
}
]
}
Nuxt.js has this awesome feature that's not available in Vue.js. But what if we want to use it in our Vue.js project too? Can we do that? And the answer is 'Yes', we can achieve it.
To get the job done, we can use this cool plugin called unplugin-vue-router
. The best part is, it's made by the same person who created vue-router itself. So, you can use it without any worry.
Implementing Nuxt Like File-Based Vue Routing:
Now, let’s implement the same feature in the Vue.js project. Here we will use the Materio Vue.js free admin template to implement the same features.
It is a perfect choice here as it supports both Typescript and Javascript. Moreover, it is based on cool tools like eslint, Typescript Vite plugins, and more. Additionally, this template even comes with really nice Vuejs components and pages that you can use to kickstart any project.
You can also clone the template from GitHub Repo directly.
How to implement file-based routing in Materio?
So, download the Materio vue admin template, and install it to see how it looks and works. We are going to use the Typescript version, but you can totally go for the JavaScript version as per your preference.
Install the node package. I'm using pnpm
package manager but feel free to use npm
or yarn
if you prefer.
# for pnpm
pnpm i
# for npm
npm install --legacy-peer-deps
To serve the template use the below command:
# for pnpm
pnpm run dev
# for npm
npm run dev
You can now access the template by typing http://localhost:5173/ in your browser's address bar.
The source code is already used vue-router
and there are some routes defined in router/routes.ts
. To switch to unplugin-vue-router
, just follow the steps below. Even if you're working on a new Vue.js project, it's no biggie! Don't worry, it's super easy!
Firstly, we need to install the unplugin-vue-router
package. Use the following command to install it.
pnpm install unplugin-vue-router
Now, we have to configure the unplugin-vue-router
in the vite.config.ts
file.
import VueRouter from 'unplugin-vue-router/vite'
export default defineConfig({
plugins: [
VueRouter({
/* options */
}),
// ⚠️ Vue must be placed after VueRouter()
Vue(),
],
})
After adding this plugin, start the dev server (usually pnpm run dev
) to generate the first version of the types at typed-router.d.ts
which should be added to your tsconfig.json
along with unplugin-vue-router/client
to types
and "moduleResolution": "Bundler"
. This is what it should look like:
{
"include": [
// other files...
"./typed-router.d.ts"
],
"compilerOptions": {
// ...
"moduleResolution": "Bundler",
"types": [
// other types...
"unplugin-vue-router/client"
]
// other options...
}
}
Now, Instead of importing from vue-router
, you should import from vue-router/auto
.
import { createApp } from 'vue'
- import { createRouter, createWebHistory } from 'vue-router'
+ import { createRouter, createWebHistory } from 'vue-router/auto'
import App from './App.vue'
const router = createRouter({
history: createWebHistory(),
// the routes property is handled by the plugin
})
createApp(App)
.use(router)
.mount('#app')
updated code only if diff is not supported in themeselection or WordPress’s code plugin.
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router/auto'
import App from './App.vue'
const router = createRouter({
history: createWebHistory(),
// the routes property is handled by the plugin
})
createApp(App)
.use(router)
.mount('#app')
Congratulations! You've successfully added file-based routing to your project. The plugin will automatically select the pages folder inside the src/pages
directory to create routes. Plus, whenever you create a vue sfc file within the pages folder, it automatically generates routes. You don't even have to worry about initialization just access it in the browser and you're good to go!
Bonus Section:
Now the bonus section, Materio Vue Admin Template gives you two cool layouts to choose from - a blank one and a default one. If you want to use the layouts system, just keep following the article ahead. To show the auto-generated routes only in the default or the blank layout, we need to filter them accordingly.
To use Materio's two different layouts, we will first figure out which pages need which layout, and for that, we use routes meta. If you're not familiar with meta, it's just a property that lets you attach random info to routes.
We can define meta in our Vue SFC file by using the definePage
macro provided by unplugin-vue-router
. In the Materio admin template, we have two auth pages - register.vue
and login.vue
. We want to use these pages with blank
layouts while rendering all other pages using the default
layout. To make it happen, we need to write the route meta like this:
<script setup lang="ts">
import { definePage } from 'vue-router/auto'
definePage({
meta: {
layout: 'blank',
},
})
</script>
<template>
<!-- ... -->
</template>
We can add type declaration for routes meta to enable autocompletion in any file.
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
layout?: 'blank' | 'default'
}
}
We will now update the router/index.ts
file as per our needs. Also, we don't need the router/routes.ts
file anymore, which had all the routes that we manually added. So, let's just remove it.
import type { App } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router/auto'
import { routes } from 'vue-router/auto-routes'
const defaultLayoutRoutes = ref<RouteRecordRaw[]>([])
const blankLayoutRoutes = ref<RouteRecordRaw[]>([])
routes.forEach(route => {
if (route.meta && route.meta.layout === 'blank')
blankLayoutRoutes.value.push(route)
else
defaultLayoutRoutes.value.push(route)
})
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{ path: '/', redirect: '/dashboard' },
{
path: '/',
component: () => import('@/layouts/default.vue'),
children: defaultLayoutRoutes.value,
},
{
path: '/',
component: () => import('@/layouts/blank.vue'),
children: blankLayoutRoutes.value,
},
],
})
export default function (app: App) {
app.use(router)
}
export { router }
In the code above, we grab all the auto-generated routes from vue-router/auto-routes
. Then we filter them out based on whether they use a blank layout. If so, we put those routes into the blankLayoutRoutes
variable. Otherwise, we put them into the defaultLayoutsRoutes
variable. These two variables are then used in the router initialization process.
Congratulations! We have successfully added a file-based routing system to our Mateio Admin template. Now, if you create any Vue SFC file inside the src/pages
folder, it will be automatically created as a route. You can easily access them in your browser without worrying about the initialization and maintenance process.
I will share a tutorial on creating a CMS App using the Bootstrap dashboard, so stay tuned.
Conclusion
In conclusion, Vue.js, with its powerful ecosystem and community support, continues to evolve, offering developers various tools and frameworks to streamline development processes. In this article, we explored the significance of vue-router in building single-page applications, and how it integrates seamlessly with Vue.js.
We have gone through the concept of file-system routing, utilized by Nuxt.js, which automates route generation based on file structure, enhancing developer productivity and project organization.
Furthermore, we demonstrated how to implement file-system routing in a Vue.js project using the unplugin-vue-router
, simplifying route management and initialization. By leveraging this approach, developers can focus more on building features rather than manual route configurations, thus accelerating development cycles.
I have prepared this article with the help of Alok Soni, a senior developer with 7 years of experience in VueJS & Deep Kumbhare.
Hope you find this article helpful and worth sharing.
Subscribe to my newsletter
Read articles from Abhijeet Dave directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Abhijeet Dave
Abhijeet Dave
A seasoned technical writer with experience in creating high-quality content. Specialized in frontend tech & simplifying complex concepts into clear articles. In my free time, I like to play chess.