Pagination SEO in Nuxt · Nuxt SEO

[NuxtSEO](https://nuxtseo.com/ "Home")

- [Modules](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [Tools](https://nuxtseo.com/tools)
- [Pro](https://nuxtseo.com/pro)
- [Learn SEO](https://nuxtseo.com/learn-seo/nuxt) [Releases](https://nuxtseo.com/releases)

[1.4K](https://github.com/harlan-zw/nuxt-seo)

[Nuxt SEO on GitHub](https://github.com/harlan-zw/nuxt-seo)

Learn SEO

Master search optimization

Nuxt

 Vue

[SEO Checklist](https://nuxtseo.com/learn-seo/checklist) [Pre-Launch Warmup](https://nuxtseo.com/learn-seo/pre-launch-warmup) [Backlinks & Authority](https://nuxtseo.com/learn-seo/backlinks)

[Mastering Meta](https://nuxtseo.com/learn-seo/nuxt/mastering-meta)

- [Titles](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/titles)
- [Meta Description](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/descriptions)
- [Image Alt Text](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/alt-text)
- [Social Sharing](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/open-graph)
- [Rich Results](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/rich-results)
- [Schema.org](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/schema-org)
- [Twitter Cards](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/twitter-cards)

[ Controlling Crawlers](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers)

- [Robots Txt](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/robots-txt)
- [Sitemaps](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/sitemaps)
- [Robot Meta Tag](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/meta-tags)
- [Canonical Link Tag](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/canonical-urls)
- [HTTP Redirects](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/redirects)
- [Duplicate Content](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/duplicate-content)
- [llms.txt](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/llms-txt)

[ Routes & Rendering](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering)

- [URL Structure](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/url-structure)
- [Pagination](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/pagination)
- [Trailing Slashes](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/trailing-slashes)
- [Query Parameters](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/query-parameters)
- [Hreflang & i18n](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/i18n)
- [404 Pages](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/404-pages)
- [Dynamic Routes](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/dynamic-routes)
- [Internal Linking](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/internal-linking)
- [Programmatic SEO](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/programmatic-seo)
- [Rendering Modes](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/rendering)
- [Security](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/security)

[ Launch & Listen](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen)

- [Getting Indexed](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/going-live)
- [Google Search Console](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/search-console)
- [Core Web Vitals](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/core-web-vitals)
- [Indexing Issues](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/indexing-issues)
- [SEO Monitoring](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/seo-monitoring)
- [Site Migration](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/site-migration)
- [IndexNow](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/indexnow)
- [Debugging](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/debugging)
- [AI Search Optimization](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen/ai-optimized-content)

1. [Learn SEO for Nuxt](https://nuxtseo.com/learn-seo)
2.
3. [Routes And Rendering](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering)
4.
5. [Pagination](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/pagination)

# Pagination SEO in Nuxt

How to implement SEO-friendly pagination in Nuxt using canonical tags, self-referencing URLs, and proper link structure.

[![Harlan Wilton](https://avatars.githubusercontent.com/u/5326365?v=4)Harlan Wilton](https://x.com/harlan-zw)14 mins read Published Dec 17, 2025

What you'll learn

- Each paginated page needs its own self-referencing canonical; don't point all to page 1
- Google deprecated `rel=prev/next` in March 2019. use crawlable `<a href>` links instead
- Infinite scroll requires hybrid approach with hidden pagination links for crawlers

Pagination splits content across multiple pages. Search engines treat each page as separate. Set canonical tags wrong and Google indexes page 1 only. Set them right and all pages rank.

Google [no longer uses](https://developers.google.com/search/blog/2011/09/pagination-with-relnext-and-relprev) `rel=prev/next` tags (deprecated March 2019). Modern pagination relies on self-referencing canonicals and crawlable `<a href>` links.

## [Self-Referencing Canonical Tags](#self-referencing-canonical-tags)

Each paginated page should have its own canonical URL pointing to itself.

![Pagination Canonical Flow](https://nuxtseo.com/images/learn-seo/vue/pagination-canonical-flow.svg)

**Don't** point all pages to page 1. That tells Google only page 1 matters, hiding pages 2+ from search results.

✅ Correct

❌ Wrong

```
<script setup lang="ts">
const route = useRoute()
const page = Number(route.query.page) || 1

useHead({
  link: [
    {
      rel: 'canonical',
      href: \`https://nuxtseo.com/blog?page=${page}\`
    }
  ]
})
</script>
```

```
<script setup lang="ts">
// Don't do this - hides pages 2+ from search
useHead({
  link: [
    {
      rel: 'canonical',
      href: 'https://nuxtseo.com/blog' // Always page 1
    }
  ]
})
</script>
```

[Self-referencing canonicals](https://www.semrush.com/blog/pagination-seo/) tell Google each page has unique content worth indexing.

## [Pagination URL Structure](#pagination-url-structure)

Use query parameters or path segments. Both work for SEO.

| URL Pattern | Example | SEO Impact |
| --- | --- | --- |
| Query parameter | `/blog?page=2` | Good - simple, flexible |
| Path segment | `/blog/page/2` | Good - cleaner URLs |
| Hash fragment | `/blog#page=2` | Bad - Google ignores `#` |

**Query parameter approach:**

Nuxt handles query parameters automatically through file-based routing:

pages/blog.vue

```
<script setup lang="ts">
const route = useRoute()
const page = computed(() => Number(route.query.page) || 1)

// Fetch paginated content
const { data: posts } = await useAsyncData(
  'posts',
  () => queryContent('/blog')
    .skip((page.value - 1) * 10)
    .limit(10)
    .find(),
  { watch: [page] }
)
</script>
```

**Path segment approach:**

Create nested route structure for cleaner URLs:

pages/blog/index.vue

```
<script setup lang="ts">
// Handles /blog
const { data: posts } = await useAsyncData(
  'posts',
  () => queryContent('/blog').limit(10).find()
)
</script>
```

pages/blog/page/[page].vue

```
<script setup lang="ts">
// Handles /blog/page/2
const route = useRoute()
const page = Number(route.params.page)

const { data: posts } = await useAsyncData(
  'posts',
  () => queryContent('/blog')
    .skip((page - 1) * 10)
    .limit(10)
    .find()
)
</script>
```

Never use fragment identifiers (`#page=2`). [Google ignores everything after `#`](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading).

## [Crawlable Links](#crawlable-links)

Google needs `<a href>` tags to discover paginated pages. Nuxt's SSR renders navigation in the initial HTML.

```
<script setup lang="ts">
const route = useRoute()
const currentPage = computed(() => Number(route.query.page) || 1)
const totalPages = 10
</script>

<template>
  <nav>
    <!-- Crawlable links with href -->
    <NuxtLink
      v-for="n in totalPages"
      :key="n"
      :to="{ query: { page: n } }"
      :class="{ active: n === currentPage }"
    >
      {{ n }}
    </NuxtLink>
  </nav>
</template>
```

`NuxtLink` generates proper `<a href>` tags while providing client-side navigation for users.

## [Pagination Component](#pagination-component)

Full example with prev/next links and numbered pages:

```
<script setup lang="ts">
const route = useRoute()
const currentPage = computed(() => Number(route.query.page) || 1)
const totalPages = 10

const prevPage = computed(() =>
  currentPage.value > 1 ? currentPage.value - 1 : null
)
const nextPage = computed(() =>
  currentPage.value < totalPages ? currentPage.value + 1 : null
)
</script>

<template>
  <nav aria-label="Pagination">
    <!-- Previous link -->
    <NuxtLink
      v-if="prevPage"
      :to="{ query: { page: prevPage } }"
    >
      Previous
    </NuxtLink>

    <!-- Page numbers -->
    <NuxtLink
      v-for="n in totalPages"
      :key="n"
      :to="{ query: { page: n } }"
      :aria-current="n === currentPage ? 'page' : undefined"
    >
      {{ n }}
    </NuxtLink>

    <!-- Next link -->
    <NuxtLink
      v-if="nextPage"
      :to="{ query: { page: nextPage } }"
    >
      Next
    </NuxtLink>
  </nav>
</template>
```

[Include links from each page to following pages](https://www.amsive.com/insights/seo/how-to-correctly-implement-pagination-for-seo-user-experience/) using `<a href>` tags. Googlebot follows these to discover your content.

## [View All Page Approach](#view-all-page-approach)

Offer a single page with all content. Point canonicals from paginated pages to the View All page.

```
<script setup lang="ts">
const route = useRoute()
const showAll = route.query.show === 'all'

useHead({
  link: [
    {
      rel: 'canonical',
      href: 'https://nuxtseo.com/blog?show=all'
    }
  ]
})
</script>

<template>
  <div>
    <NuxtLink to="/blog?show=all">
      View All
    </NuxtLink>

    <!-- Paginated content -->
    <article v-for="post in paginatedPosts" :key="post.id">
      {{ post.title }}
    </article>
  </div>
</template>
```

**Drawbacks:**

- Slow page load with 100+ items
- Poor mobile experience
- Images kill performance

[View All pages work](https://seotactica.com/learn/canonical-pagination/) for small datasets (50 items). For large catalogs, use self-referencing canonicals.

## [Infinite Scroll vs Pagination](#infinite-scroll-vs-pagination)

![Pagination vs Infinite Scroll Decision](https://nuxtseo.com/images/learn-seo/vue/pagination-vs-infinite-scroll.svg)

| Pattern | SEO Impact | UX | When to Use |
| --- | --- | --- | --- |
| Pagination | Good - all pages indexable | Predictable | Catalogs, search results |
| Infinite scroll | Poor - requires special handling | Frictionless | Social feeds, inspiration |
| Load More button | Poor - unless URL changes | Balanced | Product listings |

**Infinite scroll SEO challenges:**

[Googlebot cannot scroll](https://www.seoclarity.net/blog/pagination-vs-infinite-scroll). Content below the fold stays hidden. Solutions:

1. **Hybrid approach** - Infinite scroll for users, paginated URLs for crawlers:

```
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const posts = ref([])
const page = ref(Number(route.query.page) || 1)

async function loadMore() {
  page.value++
  const { data: newPosts } = await useFetch(\`/api/posts?page=${page.value}\`)
  posts.value.push(...newPosts.value)

  // Update URL for crawlers
  await navigateTo({ query: { page: page.value } }, { replace: true })
}

const { data: initialPosts } = await useFetch(\`/api/posts?page=${page.value}\`)
posts.value = initialPosts.value
</script>

<template>
  <div>
    <article v-for="post in posts" :key="post.id">
      {{ post.title }}
    </article>

    <button @click="loadMore">
      Load More
    </button>

    <!-- Crawlable pagination links -->
    <nav class="sr-only">
      <NuxtLink :to="{ query: { page: page + 1 } }">
        Next Page
      </NuxtLink>
    </nav>
  </div>
</template>
```

1. **Component pages** - Break infinite scroll into paginated URLs with unique canonicals. [Google recommends paginated series](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading) alongside infinite scroll.

**Load More button:**

Works for UX but [Googlebot can't click buttons](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading). Use the hybrid approach above if SEO matters.

## [Server-Side Pagination](#server-side-pagination)

Nuxt Content provides built-in pagination support:

pages/blog.vue

```
<script setup lang="ts">
const route = useRoute()
const page = computed(() => Number(route.query.page) || 1)
const limit = 10

const { data: posts } = await useAsyncData(
  'posts',
  () => queryContent('/blog')
    .skip((page.value - 1) * limit)
    .limit(limit)
    .find(),
  { watch: [page] }
)

const { data: totalPosts } = await useAsyncData(
  'totalPosts',
  () => queryContent('/blog').count()
)

const totalPages = computed(() => Math.ceil(totalPosts.value / limit))
</script>
```

For API-based pagination:

server/api/posts.get.ts

```
export default defineEventHandler(async (event) => {
  const query = getQuery(event)
  const page = Number(query.page) || 1
  const limit = 10
  const offset = (page - 1) * limit

  const posts = await db.query(
    'SELECT * FROM posts LIMIT ? OFFSET ?',
    [limit, offset]
  )

  return {
    posts,
    page,
    totalPages: Math.ceil(totalPosts / limit)
  }
})
```

Limit queries to avoid performance issues. Use database indexes on sort columns.

## [Meta Titles and Descriptions](#meta-titles-and-descriptions)

Differentiate each paginated page for SEO:

```
<script setup lang="ts">
const route = useRoute()
const page = Number(route.query.page) || 1

useHead({
  title: page === 1
    ? 'Blog Posts'
    : \`Blog Posts - Page ${page}\`,
  meta: [
    {
      name: 'description',
      content: page === 1
        ? 'Read our latest blog posts about Nuxt SEO.'
        : \`Blog posts page ${page}. Read more about Nuxt SEO.\`
    }
  ],
  link: [
    {
      rel: 'canonical',
      href: \`https://nuxtseo.com/blog${page > 1 ? \`?page=${page}\` : ''}\`
    }
  ]
})
</script>
```

Unique titles prevent [duplicate content](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/duplicate-content) confusion ([source](https://www.searchenginejournal.com/technical-seo/pagination/)).

## [Noindex on Paginated Pages](#noindex-on-paginated-pages)

**Don't** noindex paginated pages. [This loses indexed content](https://www.seoclarity.net/blog/pagination-seo). Google stops crawling noindexed pages, hiding your products/articles.

Only noindex if:

- Filter/sort variations create infinite URLs (`/blog?sort=date&order=asc&filter=nuxt`)
- You have a View All page as canonical
- Pages have no unique content

For most sites, [keep paginated pages indexable](https://ahrefs.com/blog/rel-prev-next-pagination/).

## [Common Mistakes](#common-mistakes)

**Mistake 1: Canonicalizing all pages to page 1**

```
<!-- ❌ Don't do this -->
<script setup lang="ts">
useHead({
  link: [
    { rel: 'canonical', href: 'https://nuxtseo.com/blog' }
  ]
})
</script>
```

This tells Google pages 2+ are duplicates. Use self-referencing canonicals.

**Mistake 2: Using hash fragments**

```
❌ /blog#page=2
✅ /blog?page=2
✅ /blog/page/2
```

Google ignores `#`. Your pagination won't be indexed.

**Mistake 3: Client-only pagination links**

```
<!-- ❌ Not crawlable -->
<button @click="nextPage">
Next
</button>

<!-- ✅ Crawlable -->
<NuxtLink :to="{ query: { page: page + 1 } }">
Next
</NuxtLink>
```

**Mistake 4: Inconsistent trailing slashes**

```
/blog?page=1
/blog/?page=2  ← Duplicate content
```

Pick one format. See [trailing slashes guide](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/trailing-slashes).

**Mistake 5: Blocking pagination in robots.txt**

```
# ❌ Hides paginated content
User-agent: *
Disallow: /*?page=
```

[Don't block pagination URLs](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading). Google needs to crawl them.

## [Testing Pagination SEO](#testing-pagination-seo)

**1. Check canonical tags**

```
curl -s https://nuxtseo.com/blog?page=2 | grep canonical
```

Should return:

```
<link rel="canonical" href="https://nuxtseo.com/blog?page=2">
```

**2. Verify crawlable links**

View page source (not DevTools). Look for `<a href>` tags with pagination URLs. Nuxt's SSR ensures these are present in the initial HTML.

**3. Google Search Console**

- URL Inspection tool
- Check "Coverage" report for indexed pages
- Look for paginated URLs in index

**4. Site search**

```
site:nuxtseo.com/blog inurl:page
```

Shows indexed paginated pages.

[The 2026 SEO Checklist for Nuxt & Vue Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.](https://nuxtseo.com/learn-seo/checklist) [Haven't launched yet? Start with the Pre-Launch Warmup](https://nuxtseo.com/learn-seo/pre-launch-warmup)

---

[URL Structure Create search-optimized URLs using file-based routing. Learn slug formatting, parameter handling, and route patterns that improve rankings.](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/url-structure) [Trailing Slashes Learn when to enable trailing slashes, how to configure NuxtLink, and the one-line Nuxt SEO setup.](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/trailing-slashes)

On this page

- [Self-Referencing Canonical Tags](#self-referencing-canonical-tags)
- [Pagination URL Structure](#pagination-url-structure)
- [Crawlable Links](#crawlable-links)
- [Pagination Component](#pagination-component)
- [View All Page Approach](#view-all-page-approach)
- [Infinite Scroll vs Pagination](#infinite-scroll-vs-pagination)
- [Server-Side Pagination](#server-side-pagination)
- [Meta Titles and Descriptions](#meta-titles-and-descriptions)
- [Noindex on Paginated Pages](#noindex-on-paginated-pages)
- [Common Mistakes](#common-mistakes)
- [Testing Pagination SEO](#testing-pagination-seo)

[GitHub](https://github.com/harlan-zw/nuxt-seo) [ Discord](https://discord.com/invite/275MBUBvgP)

### [NuxtSEO](https://nuxtseo.com/ "Home")

- [Getting Started](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [MCP](https://nuxtseo.com/docs/nuxt-seo/guides/mcp)

Modules

- [Robots](https://nuxtseo.com/docs/robots/getting-started/introduction)
- [Sitemap](https://nuxtseo.com/docs/sitemap/getting-started/introduction)
- [OG Image](https://nuxtseo.com/docs/og-image/getting-started/introduction)
- [Schema.org](https://nuxtseo.com/docs/schema-org/getting-started/introduction)
- [Link Checker](https://nuxtseo.com/docs/link-checker/getting-started/introduction)
- [SEO Utils](https://nuxtseo.com/docs/seo-utils/getting-started/introduction)
- [Site Config](https://nuxtseo.com/docs/site-config/getting-started/introduction)
- [Skew Protection](https://nuxtseo.com/docs/skew-protection/getting-started/introduction)
- [AI Ready](https://nuxtseo.com/docs/ai-ready/getting-started/introduction)

### [NuxtSEO Pro](https://nuxtseo.com/pro "Nuxt SEO Pro")

- [Getting Started](https://nuxtseo.com/pro)
- [Dashboard](https://nuxtseo.com/pro/dashboard)
- [Pro MCP](https://nuxtseo.com/pro/docs/getting-started/mcp-setup)

### [Learn SEO](https://nuxtseo.com/learn-seo "Learn SEO")

Nuxt

- [Mastering Meta](https://nuxtseo.com/learn-seo/nuxt/mastering-meta)
- [Controlling Crawlers](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers)
- [Launch & Listen](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen)
- [Routes & Rendering](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering)
- [Staying Secure](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/security)

Vue

- [Vue SEO Guide](https://nuxtseo.com/learn-seo/vue)
- [Mastering Meta](https://nuxtseo.com/learn-seo/vue/mastering-meta)
- [Controlling Crawlers](https://nuxtseo.com/learn-seo/vue/controlling-crawlers)
- [SPA SEO](https://nuxtseo.com/learn-seo/vue/spa)
- [SSR Frameworks](https://nuxtseo.com/learn-seo/vue/ssr-frameworks)
- [SEO Checklist](https://nuxtseo.com/learn-seo/checklist)
- [Pre-Launch Warmup](https://nuxtseo.com/learn-seo/pre-launch-warmup)
- [Backlinks & Authority](https://nuxtseo.com/learn-seo/backlinks)

### [Tools](https://nuxtseo.com/tools "SEO Tools")

- [Social Share Debugger](https://nuxtseo.com/tools/social-share-debugger)
- [Robots.txt Generator](https://nuxtseo.com/tools/robots-txt-generator)
- [Meta Tag Checker](https://nuxtseo.com/tools/meta-tag-checker)
- [HTML to Markdown](https://nuxtseo.com/tools/html-to-markdown)
- [XML Sitemap Validator](https://nuxtseo.com/tools/xml-sitemap-validator)
- [Schema.org Validator](https://nuxtseo.com/tools/schema-validator)
- [Keyword Idea Generator](https://nuxtseo.com/tools/keyword-generator)
- [Keyword Research](https://nuxtseo.com/tools/keyword-research)
- [SERP Analyzer](https://nuxtseo.com/tools/serp-analyzer)
- [Domain Rankings](https://nuxtseo.com/tools/domain-rankings)

Copyright © 2023-2026 Harlan Wilton - [MIT License](https://github.com/harlan-zw/nuxt-seo/blob/main/license) · [mdream](https://mdream.dev)