Skip to main content
Nexus Design System
GitHub

Pagination

Pagination links to the pages of a long list or table. Use it when results are split across pages. Your app decides which page numbers to show.

'use client';

import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/nxds/components/pagination/pagination';

export default function PaginationDefault() {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious href="#" />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">1</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#" isActive>
            2
          </PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">3</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationEllipsis />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">10</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationNext href="#" />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}

Installation

npx nxds-cli@latest add pagination

Run npx nxds-cli@latest init first if nxds isn’t set up in your app yet. See Install.

Usage

import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/nxds/components/pagination/pagination';

Composition

Pagination
└── PaginationContent
    └── PaginationItem
        ├── PaginationPrevious
        ├── PaginationLink
        ├── PaginationEllipsis
        └── PaginationNext

Examples

Few pages

Show every page number when they all fit, with no ellipsis.

'use client';

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/nxds/components/pagination/pagination';

export default function PaginationFewPages() {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious href="#" />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#" isActive>
            1
          </PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">2</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="#">3</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationNext href="#" />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}

As child

Pass asChild to render each link as your router's link component.

'use client';

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/nxds/components/pagination/pagination';

export default function PaginationAsChild() {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious asChild>
            <a href="#prev">Previous</a>
          </PaginationPrevious>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink asChild>
            <a href="#page-1">1</a>
          </PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink asChild isActive>
            <a href="#page-2">2</a>
          </PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationNext asChild>
            <a href="#next">Next</a>
          </PaginationNext>
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}

Props

Pagination

Pagination adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationContent

PaginationContent adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationEllipsis

PaginationEllipsis adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationItem

PaginationItem adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationLink props
PropTypeDefaultDescription
asChildbooleanfalse

Renders the child element, such as a router Link, with the link styles. Use it instead of wrapping, which would nest one <a> in another.

isActivebooleanNo default documented

Marks the link as the current page and sets aria-current="page".

size"default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null"icon"No description

PaginationNext

PaginationNext props
PropTypeDefaultDescription
asChildbooleanfalse

Renders the child element, such as a router Link, with the link styles. Use it instead of wrapping, which would nest one <a> in another.

isActivebooleanNo default documented

Marks the link as the current page and sets aria-current="page".

size"default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null"default"No description

PaginationPrevious

PaginationPrevious props
PropTypeDefaultDescription
asChildbooleanfalse

Renders the child element, such as a router Link, with the link styles. Use it instead of wrapping, which would nest one <a> in another.

isActivebooleanNo default documented

Marks the link as the current page and sets aria-current="page".

size"default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null"default"No description

Pagination links to the pages of a long list or table. Use it when results are split across pages. Your app decides which page numbers to show.

<script setup lang="ts">
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/pagination';
</script>

<template>
  <Pagination>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious href="#" />
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">1</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#" is-active>2</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">3</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationEllipsis />
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">10</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext href="#" />
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

Installation

  1. Install these components first:

    Button, ButtonGroup, Spinner.

  2. Copy and paste the following code into your project.

    components/pagination/Pagination.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface PaginationProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<PaginationProps>();
    </script>
    
    <template>
      <nav
        aria-label="pagination"
        data-slot="pagination"
        :class="cn('nx:mx-auto nx:flex nx:w-full nx:justify-center', props.class)"
      >
        <slot />
      </nav>
    </template>
    
    components/pagination/PaginationContent.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface PaginationContentProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<PaginationContentProps>();
    </script>
    
    <template>
      <ul
        data-slot="pagination-content"
        :class="
          cn(
            'nx:m-0 nx:flex nx:list-none nx:flex-row nx:items-center nx:gap-1 nx:p-0',
            props.class
          )
        "
      >
        <slot />
      </ul>
    </template>
    
    components/pagination/PaginationEllipsis.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { IconDots } from '../../lib/icons';
    import { cn } from '../../lib/utils';
    
    export interface PaginationEllipsisProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<PaginationEllipsisProps>();
    </script>
    
    <template>
      <span
        aria-hidden="true"
        data-slot="pagination-ellipsis"
        :class="
          cn('nx:flex nx:items-center nx:justify-center nx:size-10', props.class)
        "
      >
        <IconDots class="nx:size-4" />
        <span class="nx:sr-only">More pages</span>
      </span>
    </template>
    
    components/pagination/PaginationItem.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    export interface PaginationItemProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<PaginationItemProps>();
    </script>
    
    <template>
      <li data-slot="pagination-item" :class="props.class">
        <slot />
      </li>
    </template>
    
    components/pagination/PaginationLink.vue
    <script setup lang="ts">
    import { Primitive, type PrimitiveProps } from 'reka-ui';
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    import { type ButtonVariants, buttonVariants } from '../button';
    
    export interface PaginationLinkProps extends PrimitiveProps {
      size?: ButtonVariants['size'];
      /**
       * Marks the link as the current page and sets `aria-current="page"`.
       */
      isActive?: boolean;
      class?: HTMLAttributes['class'];
    }
    
    const props = withDefaults(defineProps<PaginationLinkProps>(), {
      as: 'a',
      size: 'icon',
      isActive: undefined,
    });
    </script>
    
    <template>
      <Primitive
        :aria-current="isActive ? 'page' : undefined"
        data-slot="pagination-link"
        :data-active="isActive"
        :as="as"
        :as-child="asChild"
        :class="
          cn(
            buttonVariants({ variant: isActive ? 'outline' : 'ghost', size }),
            'nx:typography-label-default',
            props.class
          )
        "
      >
        <slot />
      </Primitive>
    </template>
    
    components/pagination/PaginationNext.vue
    <script setup lang="ts">
    import { IconChevronRight } from '../../lib/icons';
    import type { ButtonVariants } from '../button';
    
    import PaginationLink, { type PaginationLinkProps } from './PaginationLink.vue';
    
    export interface PaginationNextProps extends Omit<PaginationLinkProps, 'size'> {
      size?: ButtonVariants['size'];
    }
    
    const props = withDefaults(defineProps<PaginationNextProps>(), {
      as: 'a',
      size: 'default',
      isActive: undefined,
    });
    </script>
    
    <template>
      <PaginationLink aria-label="Go to next page" v-bind="props">
        <slot>
          <span>Next</span>
          <IconChevronRight />
        </slot>
      </PaginationLink>
    </template>
    
    components/pagination/PaginationPrevious.vue
    <script setup lang="ts">
    import { IconChevronLeft } from '../../lib/icons';
    import type { ButtonVariants } from '../button';
    
    import PaginationLink, { type PaginationLinkProps } from './PaginationLink.vue';
    
    export interface PaginationPreviousProps extends Omit<
      PaginationLinkProps,
      'size'
    > {
      size?: ButtonVariants['size'];
    }
    
    const props = withDefaults(defineProps<PaginationPreviousProps>(), {
      as: 'a',
      size: 'default',
      isActive: undefined,
    });
    </script>
    
    <template>
      <PaginationLink aria-label="Go to previous page" v-bind="props">
        <slot>
          <IconChevronLeft />
          <span>Previous</span>
        </slot>
      </PaginationLink>
    </template>
    
    components/pagination/index.ts
    export { default as Pagination, type PaginationProps } from './Pagination.vue';
    export {
      default as PaginationContent,
      type PaginationContentProps,
    } from './PaginationContent.vue';
    export {
      default as PaginationEllipsis,
      type PaginationEllipsisProps,
    } from './PaginationEllipsis.vue';
    export {
      default as PaginationItem,
      type PaginationItemProps,
    } from './PaginationItem.vue';
    export {
      default as PaginationLink,
      type PaginationLinkProps,
    } from './PaginationLink.vue';
    export {
      default as PaginationNext,
      type PaginationNextProps,
    } from './PaginationNext.vue';
    export {
      default as PaginationPrevious,
      type PaginationPreviousProps,
    } from './PaginationPrevious.vue';
    
  3. Keep each file at the path shown above it.

    Paths are relative to packages/vue/src/. Keep the same folders under your @/ root, and the imports between files work without edits.

Usage

import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from '@/components/pagination';

Examples

Few pages

Show every page number when they all fit, with no ellipsis.

<script setup lang="ts">
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/pagination';
</script>

<template>
  <Pagination>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious href="#" />
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#" is-active>1</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">2</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">3</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext href="#" />
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

As child

Pass as-child to render each link as your router's link component.

<script setup lang="ts">
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/pagination';
</script>

<template>
  <Pagination>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious as-child>
          <a href="#prev">Previous</a>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink as-child>
          <a href="#page-1">1</a>
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink as-child is-active>
          <a href="#page-2">2</a>
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext as-child>
          <a href="#next">Next</a>
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

Props

Pagination

Pagination adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationContent

PaginationContent adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationEllipsis

PaginationEllipsis adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationItem

PaginationItem adds no props of its own; it accepts the props of the element or primitive it wraps.

PaginationLink props
PropTypeDefaultDescription
isActivebooleanNo default documented

Marks the link as the current page and sets aria-current="page".

size"default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null"icon"No description

PaginationNext

PaginationNext props
PropTypeDefaultDescription
isActivebooleanNo default documented

Marks the link as the current page and sets aria-current="page".

size"default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null"default"No description

PaginationPrevious

PaginationPrevious props
PropTypeDefaultDescription
isActivebooleanNo default documented

Marks the link as the current page and sets aria-current="page".

size"default" | "sm" | "lg" | "icon-sm" | "icon" | "icon-lg" | null"default"No description