
Server-Driven UI framework on the Web: Examples, Benefits & Use Cases
29 Oct 2022
It takes a minute
Free support
14 days free trial
“Hi guys. I have a question: if we have Nuxt.js, which provides a lot of nice stuff out of the box, why would we use plain Vue.js?” - Reddit User.
“*I’m starting a new project, should I use Vue or Nuxt*?*” -* Developer.
Do you have similar questions and thoughts of people in these Nuxt vs Vue reddit posts? If you do, don’t worry, you're not alone. Almost all Vue developers have asked these questions before.
So, what do you choose for your project?
In this article, I am gonna review both frontend frameworks, discuss their use cases, features, and benefits. I will look at real-world scenarios where choosing between Vue and Nuxt is hard. I will also challenge popular beliefs like Nuxt is better for SEO compared to Vue.
This is the only article you need to get the knowledge to make a decision as to whether to use Vue or Nuxt.
Before we compare them, it's important to understand that Vue.js and Nuxt.js aren't competing alternatives but rather complementary tools with different purposes:
Vue.js is a flexible and progressive JavaScript framework for building user interfaces. It's focused on the view layer and gives developers full control over application architecture.
Nuxt.js is a higher-level framework built on top of Vue that provides structure, conventions, and additional features while taking away common configuration tasks that were needed in Vue.
The key point here is that Nuxt is built on top of Vue, which means if you’re trying to learn or work with Nuxt, you need to know the basics of Vue.
Now let’s break down the differences between Vue and Nuxt. The differences will also highlight the benefits and added Nuxt features.
I will separate the differences into 5 topics, these are :
1. Developer Experience: Configuration vs. Convention
2. Layout system
3. Rendering Approaches and their impact on SEO: More Than Just SSR vs. SPA
4. Performance Optimization.
5. Ecosystem
Let’s look at how these two frameworks handle the application scaffolding.
Vue.js: Maximum flexibility, more setup work
With Vue.js, you start with a clean slate and configure everything yourself:
// Vue project structure - you decide what goes where my-vue-project/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── router/ // Manual router setup required │ │ └── index.js │ ├── store/ // Manual Pinia setup if needed │ │ └── index.js │ ├── views/ │ ├── App.vue │ └── main.js ├── package.json └── vue.config.js // Optional configuration
The router setup requires explicit configuration:
// src/router/index.js in Vue import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import About from '../views/About.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: About } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
With Vue, you have to set up your folders manually and define which folder should hold a functionality.
For example, you can decide to create a folder named views to hold the pages of the application(as in the case above), or you can name that folder pages. This is quite helpful if you’re coming from a framework or technology that has different folder naming methodologies; you can quickly adjust to Vue since you have control over what to name your folders.
With Vue, you must manually set up the Vue router and define your routes manually. This can quickly become cumbersome when you have a lot of pages in your web applications, for example if you have a website that has 50 pages you will have to manually first of all create the page in the views folder, after that you import that page into your route file and then define the route that will link to that page.
While this routing pattern is cumbersome, it offers flexibility and more control over how your routes are defined.
Nuxt.js provides a simpler and practical approach that replaces the manual configuration in Vue.js. Nuxt builds on Vue by replacing the manual folder structure with a predefined folder structure.
// Nuxt folder structure - follow the conventions my-nuxt-project/ ├── assets/ ├── components/ ├── layouts/ // Predefined layout system ├── middleware/ // Route middleware(More on this later on) ├── pages/ // Automatic routing based on file structure │ ├── index.vue // Maps to / │ └── inspire.vue // Maps to /about ├── plugins/ ├── static/ ├── store/ // Auto-initialized pinia modules ├── nuxt.config.js └── package.json
When you create a Nuxt project, you get all of these out-of-the-box so you can bid goodbye to manual configuration of routes, and also you stop worrying about folder names or a proper structure for your application. Nuxt does the configurations for you under the hood and then gives you the conventions to use.
💡Key takeaway: Vue.js offers you more control over your application configuration, while Nuxt does the setup for you under the hood, taking away manual configuration and rather providing you with conventions to use.
Let’s say you have an e-commerce website, and you have about 10 pages in total, excluding dynamic pages for product details.
For these 10 pages you want to have different layout for each page, for example on the home page, you want to have the Header, Homepage dynamic Content and then a footer, but for the legal page and privacy policy page you do not want the header at all, just the page content and then a footer.
For the product details page, you want a different header altogether, and you want a new arrivals section before your footer. So, 3 different layouts with the homepage layout being the one default layout.
How do you accomplish this in Vue and Nuxt?
In Vue, you have to manually set this up. Remember, Vue is all about configuration and handing over to you the small details to set things up yourself. So, let’s see a high-level overview of how to create a layout system in Vue.
1. You’ll need to create a layout file in a new layouts folder:
// src/layouts/AppContainerLayout.vue <template> <component :is="this.$route.meta.layoutComponent"> <slot /> </component> </template> <script> export default { name: "AppContainerLayout" } </script>
2. In your app.vue
file, you’ll have to include this layout:
// src/App.vue <script setup> import AppContainerLayout from './layouts/AppContainerLayout.vue'; </script> <template> <AppContainerLayout> <RouterView /> </AppContainerLayout> </template>
3. Next, you setup a route metatag
to use the layout that you have created.
// src/router/router.js import { createRouter, createWebHistory } from "vue-router"; import { loadLayoutMiddleware } from "./middleware/loadLayoutMiddleware"; import HomeView from "../views/HomeView.vue"; import AnotherView from "../views/AnotherView.vue"; const routes = [ { path: "/", name: "Home", component: HomeView, meta: { layout: "AppLayoutDashboard", }, }, { path: "/another_view", name: "Another", component: AnotherView, }, ]; const router = createRouter({ history: createWebHistory(), routes, }); router.beforeEach(loadLayoutMiddleware); export default router;
4. Load the default layout when no layout is declared within the route:
// src/router/middleware/loadLayoutMiddleware.js export async function loadLayoutMiddleware(route) { try { let layout = route.meta.layout || "DefaultLayout"; let layoutComponent = await import(`@/layouts/${layout}.vue`); route.meta.layoutComponent = layoutComponent.default; } catch (e) { console.error("Error occurred in processing of layouts: ", e); console.log("Mounted DefaultLayout"); let layout = "DefaultLayout"; let layoutComponent = await import(`@/layouts/${layout}.vue`); route.meta.layoutComponent = layoutComponent.default; } }
5. You can now finally use the HomeView component, and it should display the layouts.
// src/views/HomeView.vue: <template> <!-- VIEW CODE HERE --> </template>
Well, that was quite a process. Let’s see how Nuxt handles Layouts
Remember the Layouts folder you get when you create a Nuxt project? That folder contains a default.vue
file, which is the automatically generated layout for you.
In this folder, you can create multiple layouts of your choice.
├── layouts/ │ ├── default.vue │ └── layout-one.vue
After creating these layouts, you simply go to the page you want to use that layout on. For example, on the product-details pag,e you can use the layout like so:
├── pages/ │ ├── index.vue │ └── product-details.vue
Then in the script tag of your page you can just do:
// product-details.vue <script setup> definePageMeta({ layout: 'layout-one' }) // An empty layout value will default to the default layout.
With that, you have successfully created a layout and used it easily in Nuxt.
💡Key takeaway: Layouts in Vue are quite configure and will require you reading through the docs almost all the time to remember how to create and use them. But in Nuxt it is very easy since it does all the work for you and you can just create the layouts and use them easily.
If you have a project that requires different layouts for different screens, Nuxt will be the better idea since the extra complexity in Vue is not worth it except in very rare cases where you have a need that Nuxt built in layout system does not cover.
When it comes to the world of frontend development, you’ll hear these terms a lot with regard rendering. To better understand how Vue and Nuxt contrast, let’s define these rendering terms in simple, easy-to-understand language, using kindergarten terms.
Imagine you ask your teacher (a server) to draw a picture for you. The teacher draws the complete picture and hands it to you, ready to view. You get the finished picture immediately!
In web terms: The server prepares the entire webpage before sending it to your browser. This means faster initial page loads and better SEO since search engines see the complete page.
Imagine a magic coloring book where you never turn pages. Instead, when you click on different sections in the contents, the current page transforms into the new one right before your eyes.
In web terms: Your browser loads the website shell once, then JavaScript swaps content in and out as you navigate, without full page reloads. This creates smooth transitions but requires more initial JavaScript download. This is also very bad for SEO since the browser doesn’t have the complete page. The only Search engine visibility you’ll get is the homepage.
Imagine your teacher prepares all the drawings for the entire class before school starts. When you arrive, your picture is already finished and waiting for you.
In web terms: All pages are pre-rendered during build time (before deployment). This creates static HTML files ready to serve instantly to any visitor, making it super fast, SEO friendlier since the browser already has the pages so it can crawl, but less dynamic. SSG will not be good for a page that has content changing constantly for example a blog page, this means you will be building and redeploying the page each time the blog gets updated.
Imagine some classroom activities have pre-drawn pictures (like complex animals), while others give you blank paper to draw yourself.
In web terms: Some pages use SSG for static content that rarely changes, while other pages use SSR or SPA approaches for dynamic content, giving you the best of both worlds. So it’s just a mix of SSR and SSG in one application. So you can have an SSG rendering for your application but choose to render your blog section using SSR.
Imagine drawing a picture together with your teacher. The teacher starts the drawing (server), then gives it to you with special markers that let you add more details (client).
In web terms: Content renders first on the server for fast initial loading, then the browser takes over to add interactivity. This provides good performance and SEO while maintaining rich functionality.
Now you understand these terms, let’s see which of these, Vue and Nuxt, offers us.
A common misconception is that Vue is only for SPAs while Nuxt is only for server-side rendering. In reality, both offer multiple rendering modes with different approaches. Let’s take a look at the rendering options both frameworks present.
Let's start with Vue.
Client-Side Rendering (CSR) Or SPA: The default approach, with all rendering happening in the browser.
💡Performance Impacts: Initial page load may be slower if your page is large, but once the page is loaded, it is extremely fast to navigate since it has all been downloaded
Server-Side Rendering (SSR): Possible using Vue's SSR guide, but requires manual setup with Node.js
// server.js for Vue SSR const fs = require('fs') const path = require('path') const express = require('express') const { createBundleRenderer } = require('vue-server-renderer') const server = express() const template = fs.readFileSync('./index.template.html', 'utf-8') const serverBundle = require('./dist/vue-ssr-server-bundle.json') const clientManifest = require('./dist/vue-ssr-client-manifest.json') const renderer = createBundleRenderer(serverBundle, { template, clientManifest }) server.use('/dist', express.static(path.join(__dirname, './dist'))) server.get('*', (req, res) => { const context = { url: req.url } renderer.renderToString(context, (err, html) => { if (err) { // Handle error return res.status(500).end('Server Error') } res.end(html) }) }) server.listen(8080)
Now, that is a lot of configuration to get SSR to work in Vue.
💡If you need SSR benefits, you might consider looking away from Vue, except you fancy the overhead of configuring it yourself.
Nuxt makes multiple rendering strategies accessible through simple configuration:
Universal Mode (SSR + Client hydration): Server-renders on first load, then behaves like an SPA
Single Page Application Mode: Traditional client-side rendering3.
SSG: Pre-renders all routes at build time
Hybrid Rendering: Mix of SSR, SPA, and static based on page needs (since Nuxt 3). This means that for each page, you can decide what type of rendering you want, cool right :)
Switching between modes in Nuxt is straightforward:
// nuxt.config.js export default { // For SSR (universal mode) ssr: true, // For static site generation target: 'static', // For SPA mode ssr: false }
In Nuxt, you can control rendering per-page using the definePageMeta
function with the renderingMode
property in your page's script section. Here's how to make a specific blog page use SSR while the rest of your site uses SSG:
<script setup> definePageMeta({ // This tells Nuxt to use SSR specifically for this page renderingMode: 'server' }) // Your regular page script code continues here const { data: blogPost } = await useFetch('/api/blog/latest') </script> <template> <div class="blog-post"> <h1>{{ blogPost.title }}</h1> <div v-html="blogPost.content"></div> </div> </template>
So while other pages use SSG, you can use SSR to render your blog page. How sweet.
This is only partially true. While Nuxt does make SSR and static generation easier to implement, Vue applications can achieve the same SEO benefits with proper configuration. The difference is in development effort, not capability. A well-optimized Vue SPA with proper meta tag management and pre-rendering can perform well for SEO.
With Vue, performance optimizations are powerful but require manual implementation:
Code Splitting: Handled through webpack/Vite configuration and async components
Asset Optimization: Manual setup required for image optimization pipelines
Bundle Optimization: Direct control over webpack/Vite configuration
Example of lazy-loading routes in Vue:
// Router with lazy loading in Vue const routes = [ { path: '/dashboard', name: 'Dashboard', component: () => import(/* webpackChunkName: "dashboard" */ '../views/Dashboard.vue') }, { path: '/reports', name: 'Reports', component: () => import(/* webpackChunkName: "reports" */ '../views/Reports.vue') } ]
Nuxt includes many performance optimizations out of the box:
Automatic Code Splitting: Every page becomes a separate chunk
Image Optimization: Built-in with `@nuxt/image` module
Automatic Component Import: No need for repetitive imports
Link Prefetching: Every page linked to a page is automatically fetched even when the page is yet to be navigated to thereby improving speed.
Example of image optimization in Nuxt:
<!-- Optimized image in Nuxt with @nuxt/image --> <template> <nuxt-img src="/product-image.jpg" width="600" height="400" format="webp" loading="lazy" alt="Product image" /> </template>
Optimizations for speed and other functionality, like route lazy loading, are done manually and can sometimes become complex, but with Nuxt, everything is built in.
Vue has cultivated a rich ecosystem of libraries and tools that developers can selectively integrate:
UI Libraries: Vuetify, Quasar, Element UI, PrimeVue, and Buefy offer polished component collections
State Management: Multiple options including Pinia, Vuex, and simpler alternatives like Harlem
Form Validation: VeeValidate, Vuelidate for form handling
Testing: Vue Test Utils, Vue Testing Library, and Cypress integration
Router: Vue Router provides the standard routing solution
Vueuse: Collection of Vue Composition Utilities for common stuff like useDateFormat
With Vue, you choose each piece of the ecosystem independently and integrate them manually:
// Manually integrating ecosystem libraries in Vue import { createApp } from 'vue' import { createPinia } from 'pinia' import { createRouter, createWebHistory } from 'vue-router' import Vuelidate from 'vuelidate' import Vuetify from 'vuetify' import App from './App.vue' import routes from './router/routes' const app = createApp(App) const router = createRouter({ history: createWebHistory(), routes }) app.use(createPinia()) app.use(router) app.use(Vuelidate) app.use(Vuetify) app.mount('#app')
Nuxt's ecosystem is built around its modular architecture, with official and community modules that seamlessly integrate into your application while also extending Vue’s libraries:
Nuxt Modules System: A plugin system that hooks into Nuxt's lifecycle and extends its functionality, it has 180+ community modules addressing common needs
Official Modules: Content, Image, Auth, PWA, i18n, and more, all officially maintained
Integrating functionality through Nuxt modules requires minimal configuration:
// nuxt.config.js - Adding modules export default { modules: [ '@nuxtjs/axios', '@nuxt/content', '@nuxt/image', '@nuxtjs/i18n', '@nuxtjs/pwa', ], // Module-specific configuration axios: { baseURL: 'https://api.example.com' }, content: { markdown: { prism: { theme: 'prism-themes/themes/prism-material-oceanic.css' } } } }
Vue has a wide range of plugins that address common needs. Nuxt extends that by adding modules for simple integrations for frequently used tools.
To decide between Vue and Nuxt, answer these questions:
1. How important is initial development speed?
- If rapid development with less boilerplate is critical, Nuxt has the edge.
2. How essential is SEO for your project?
- If SEO is critical, Nuxt makes implementation easier, though Vue can achieve the same with more effort, but that will become really slow if you include development speed criteria.
If, for example, you want to develop the MVP of an e-commerce application that thrives on SEO in less than a week, Vue will be a bad choice, considering that e-commerce applications have different pages, will grow overtime and are complex, so creating different SSR setups can be time consuming and quickly become stressful. Nuxt is a clear winner in this case.
3. How conventional is your project architecture?
- If your project fits standard patterns, Nuxt's conventions will help.
- For highly specialized architectures, Vue's flexibility may be worth the extra setup.
4. What is your team's experience level?
- For teams new to the Vue ecosystem, Nuxt provides helpful guardrails.
- For Vue experts, either option works well, and this shouldn’t be a consideration point.
5. How much do you value architectural control vs. development speed?
- Vue offers maximum control but requires more implementation time.
- Nuxt offers speed and features at the cost of some flexibility.
6. Will your project require 3rd party libraries/tools?
- Nuxt has a suite of tools in its module system.
Case Study 1: Restaurant Website
Project Requirements: Content-focused site needing excellent SEO, regular content updates by non-technical staff, and fast initial load times.
Chosen Solution: Nuxt with SSG with SSR for the food menu and listing.
Why: The restaurant team needed to update content(food listing, menu, discounts, etc..) without developer intervention. Nuxt's file-based routing, combined with a headless CMS BCMS, allowed content editors to create new pages easily while maintaining optimal performance through static generation. SEO was critical, making pre-rendered HTML essential.
If you wanna learn how you can build something like this, take a look at this article that teaches you how to use Nuxt with Tailwind CSS to build a restaurant website using BCMS as the backend.
Case Study 2: Enterprise Dashboard Application
Project Requirements: Complex internal tool with numerous interactive components, role-based permissions, and complex state management.
Chosen Solution: Vue.js with custom architecture
Why: The dashboard required a highly customized architecture not easily mapped to Nuxt's conventions. The development team needed granular control over code splitting, state management patterns, and custom authentication flows that would have required significant customization in Nuxt.
Key Implementation Detail: By using Vue's composition API extensively with custom composables for business logic, the team achieved better code organization than wouldn’t have been possible with Nuxt's more opinionated structure.
The most pragmatic developers recognise that Vue and Nuxt excel in different scenarios. Many successful teams use:
- Nuxt for content-focused websites, marketing sites, blogs, and applications where SEO matters
- Vue for highly interactive applications, specialized admin interfaces, and projects requiring unconventional architecture
It's also worth noting that Many companies run both frameworks simultaneously. Many companies use Nuxt for their public-facing sites and Vue for internal tools, leveraging the best of both worlds while maintaining knowledge transferability across teams.
The most important factor is aligning your choice with your specific project needs rather than following trends. Both Vue and Nuxt are production-ready tools capable of scaling to enterprise requirements when used appropriately for their strengths.
Whatever you pick, though, BCMS is a great choice for content management.
Get all the latest BCMS updates, news and events.
By submitting this form you consent to us emailing you occasionally about our products and services. You can unsubscribe from emails at any time, and we will never pass your email to third parties.
There are many actionable insights in this blog post. Learn more: