Quick Start Tutorial
Build a working blog with WPNuxt from scratch. This tutorial covers fetching posts, displaying content, and creating dynamic pages.
Prerequisites
- Node.js 20+
- A WordPress site with WPGraphQL installed
- Basic familiarity with Vue and Nuxt
https://wordpress.wpnuxt.comStep 1: Create a Nuxt Project
npx nuxi@latest init my-wpnuxt-blog
cd my-wpnuxt-blog
pnpm install
Step 2: Install WPNuxt
pnpm add @wpnuxt/core
Step 3: Configure
Create or update nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@wpnuxt/core'],
wpNuxt: {
wordpressUrl: 'https://wordpress.wpnuxt.com'
}
})
Or use an environment variable:
WPNUXT_WORDPRESS_URL=https://wordpress.wpnuxt.com
Step 4: Generate Types
Run prepare to download the GraphQL schema and generate types:
pnpm nuxt prepare
You should see:
[wpnuxt] WPNuxt module loaded in XXXms
Step 5: Create the Home Page
Replace app.vue with a simple layout, then create the home page.
<template>
<div class="container">
<NuxtPage />
</div>
</template>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
</style>
Create the pages directory and home page:
<script setup lang="ts">
// Fetch the 10 most recent posts
const { data: posts } = await usePosts({ first: 10 })
</script>
<template>
<div>
<h1>My Blog</h1>
<div v-if="posts?.length">
<article v-for="post in posts" :key="post.id">
<h2>
<NuxtLink :to="post.uri">
{{ post.title }}
</NuxtLink>
</h2>
<p>{{ post.date }}</p>
<div v-sanitize-html="post.excerpt" />
</article>
</div>
<p v-else>No posts found.</p>
</div>
</template>
<style scoped>
article {
margin-bottom: 2rem;
padding-bottom: 2rem;
border-bottom: 1px solid #eee;
}
h2 {
margin-bottom: 0.5rem;
}
h2 a {
color: inherit;
text-decoration: none;
}
h2 a:hover {
text-decoration: underline;
}
</style>
Step 6: Start the Dev Server
pnpm dev
Open http://localhost:3000. You should see a list of blog posts!
usePosts() blocks navigation until data is ready — this means the page won't render until posts are loaded, which is ideal for SEO (search engines see the full content). For non-critical content like sidebars, use { lazy: true } to load data in the background without blocking. See Fetching Data for details.Step 7: Create the Single Post Page
Create a dynamic route for individual posts:
<script setup lang="ts">
const route = useRoute()
// Fetch post or page by URI
const { data: node } = await useNodeByUri({ uri: route.path })
// Handle 404
if (!node.value) {
throw createError({
statusCode: 404,
message: 'Page not found'
})
}
// Set page title
useHead({
title: node.value.title
})
</script>
<template>
<article v-if="node">
<h1>{{ node.title }}</h1>
<p v-if="'date' in node" class="date">
Published: {{ new Date(node.date).toLocaleDateString() }}
</p>
<div v-sanitize-html="node.content" class="content" />
<NuxtLink to="/">← Back to home</NuxtLink>
</article>
</template>
<style scoped>
.date {
color: #666;
margin-bottom: 2rem;
}
.content {
line-height: 1.7;
}
.content :deep(img) {
max-width: 100%;
height: auto;
}
</style>
Click on a post title on the home page — you'll now see the full post content!
[...slug].vue file is a Nuxt catch-all route. It matches any URL path (e.g. /hello-world, /about, /blog/my-post). The useNodeByUri() composable sends that path to WordPress and asks "what content lives at this URI?" — WordPress returns a post, page, or any content type registered at that URL.Step 8: Add a Navigation Menu
Fetch a WordPress menu and display it:
<script setup lang="ts">
// Fetch menu by name (created in WordPress Appearance → Menus)
const { data: menuItems } = await useMenu({ name: 'Primary Menu' })
</script>
<template>
<nav v-if="menuItems?.length">
<NuxtLink
v-for="item in menuItems"
:key="item.id"
:to="item.uri"
>
{{ item.label }}
</NuxtLink>
</nav>
</template>
<style scoped>
nav {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #eee;
}
a {
color: inherit;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
Add to your layout:
<template>
<div class="container">
<SiteNav />
<NuxtPage />
</div>
</template>
name parameter matches the menu name (or slug).Step 9: Add Featured Images (Optional)
Show featured images on the post list:
<script setup lang="ts">
const { data: posts } = await usePosts({ first: 10 })
</script>
<template>
<div>
<h1>My Blog</h1>
<div v-if="posts?.length">
<article v-for="post in posts" :key="post.id">
<img
v-if="post.featuredImage?.node?.sourceUrl"
:src="post.featuredImage.node.sourceUrl"
:alt="post.featuredImage.node.altText"
class="featured-image"
/>
<h2>
<NuxtLink :to="post.uri">
{{ post.title }}
</NuxtLink>
</h2>
<div v-sanitize-html="post.excerpt" />
</article>
</div>
</div>
</template>
<style scoped>
.featured-image {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 1rem;
}
</style>
What You've Learned
In this tutorial you've used the key building blocks of a WPNuxt project:
usePosts()— Fetch a list of posts with auto-generated composablesuseNodeByUri()— Resolve any WordPress URL to its contentuseMenu()— Fetch editor-managed navigation menus[...slug].vue— Catch-all route pattern for dynamic WordPress contentv-sanitize-html— Safely render WordPress HTML content
What's Next?
You now have a working blog! Here's where to go from here:
- How WPNuxt Works — Understand the architecture and composable generation pipeline
- Custom Queries — Fetch exactly the data you need
- Rendering Blocks — Use Gutenberg blocks as Vue components
- Caching — Optimize performance
- Deployment — Deploy to production
Complete Code
Here's the final project structure:
my-wpnuxt-blog/
├── app.vue
├── nuxt.config.ts
├── pages/
│ ├── index.vue
│ └── [...slug].vue
├── components/
│ └── SiteNav.vue
└── .env
Troubleshooting
"Cannot find module" errors
Run pnpm nuxt prepare to regenerate types.
No posts showing
- Check that WordPress has published posts
- Verify WPGraphQL is installed and activated
- Test the GraphQL endpoint:
https://your-wordpress.com/graphql
Menu not loading
- Create a menu in WordPress under Appearance → Menus
- Assign it to the "Primary" location
- Check that menu items are published pages/posts