Vue is elegant. But building a full-stack app means configuring Vue Router, Pinia, SSR, API routes, and a build system. Nuxt handles all of that — you just write Vue components.
Nuxt 4 is the Next.js of the Vue ecosystem — batteries included, conventions over configuration.
Quick Start
npx nuxi@latest init my-app
cd my-app && npm install && npm run dev
Auto-Imports — No Import Statements Needed
<!-- pages/index.vue — no imports! -->
<script setup lang="ts">
// ref, computed, watch — auto-imported from Vue
const count = ref(0);
const doubled = computed(() => count.value * 2);
// useFetch — auto-imported from Nuxt
const { data: posts } = await useFetch("/api/posts");
// useRoute — auto-imported
const route = useRoute();
</script>
<template>
<div>
<h1>Count: {{ count }} (doubled: {{ doubled }})</h1>
<button @click="count++">Increment</button>
<PostList :posts="posts" /> <!-- Components are auto-imported too -->
</div>
</template>
No import { ref, computed } from 'vue'. No import PostList from '@/components/PostList.vue'. Nuxt auto-imports everything.
File-Based Routing
pages/
index.vue ← /
about.vue ← /about
blog/
index.vue ← /blog
[slug].vue ← /blog/:slug
users/
[id]/
index.vue ← /users/:id
settings.vue ← /users/:id/settings
Server API Routes
// server/api/posts.get.ts
export default defineEventHandler(async (event) => {
const posts = await db.posts.findMany({ take: 20 });
return posts;
});
// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const post = await db.posts.create({ data: body });
return post;
});
// server/api/posts/[id].get.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, "id");
const post = await db.posts.findUnique({ where: { id } });
if (!post) throw createError({ statusCode: 404, message: "Not found" });
return post;
});
Data Fetching
<script setup lang="ts">
// SSR-friendly data fetching — runs on server, hydrates on client
const { data: user, pending, error } = await useFetch("/api/user");
// Lazy fetch — doesn't block navigation
const { data: notifications } = useLazyFetch("/api/notifications");
// With caching key
const { data: product } = await useFetch(`/api/products/${route.params.id}`, {
key: `product-${route.params.id}`,
});
</script>
Nuxt 4 vs Next.js 15
| Feature | Nuxt 4 | Next.js 15 |
|---|---|---|
| UI framework | Vue 3 | React 19 |
| Auto-imports | Yes | No |
| File routing | Yes | Yes |
| Server API | Built-in (Nitro) | API routes |
| State management | useState composable | External (Zustand/etc) |
| DevTools | Built-in | External |
| Deploy targets | 15+ via Nitro | Vercel-optimized |
Choose Nuxt if you prefer Vue. Choose Next.js if you prefer React. Both are excellent.
Start here: nuxt.com
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)