API Reference / InstantSearch.js / pagination
Signature
instantsearch.widgets.pagination({
  container: string|HTMLElement,
  // Optional parameters
  showFirst: boolean,
  showPrevious: boolean,
  showNext: boolean,
  showLast: boolean,
  padding: number,
  totalPages: number,
  scrollTo: string|HTMLElement|boolean,
  templates: object,
  cssClasses: object,
});

About this widget # A

The pagination widget displays a pagination system allowing the user to change the current page.

The Algolia search engine limits paginating to 1,000 hits per page.

Examples # A

1
2
3
instantsearch.widgets.pagination({
  container: '#pagination',
});

Options # A

container #
type: string|HTMLElement
Required

The CSS Selector or HTMLElement to insert the widget into.

1
2
3
instantsearch.widgets.pagination({
  container: '#pagination',
});
showFirst #
type: boolean
default: true
Optional

Whether to display the first page link.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  showFirst: false,
});
showPrevious #
type: boolean
default: true
Optional

Whether to display the previous page link.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  showPrevious: false,
});
showNext #
type: boolean
default: true
Optional

Whether to display the next page link.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  showNext: false,
});
showLast #
type: boolean
default: true
Optional

Whether to display the last page link.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  showLast: false,
});
padding #
type: number
default: 3
Optional

The number of pages to display on each side of the current page.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  padding: 2,
});
totalPages #
type: number
Optional

The maximum number of pages to browse.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  totalPages: 2,
});
scrollTo #
type: string|HTMLElement|boolean
default: body
Optional

Where to scroll after a click. Set to false to disable.

1
2
3
4
instantsearch.widgets.pagination({
  // ...
  scrollTo: 'header',
});
templates #
type: object

The templates to use for the widget.

1
2
3
4
5
6
instantsearch.widgets.pagination({
  // ...
  templates: {
    // ...
  },
});
cssClasses #
type: object
default: {}

The CSS classes to override.

  • root: the root element of the widget.
  • noRefinementRoot: the root container without results.
  • list: the list of results.
  • item: the item in the list of results.
  • firstPageItem: the first item.
  • lastPageItem: the last item.
  • previousPageItem: the previous item.
  • nextPageItem: the next item.
  • pageItem: the page items.
  • selectedItem: the selected item.
  • disabledItem: the disabled item.
  • link: the link elements.
1
2
3
4
5
6
7
8
9
10
instantsearch.widgets.pagination({
  // ...
  cssClasses: {
    root: 'MyCustomPagination',
    list: [
      'MyCustomPaginationList',
      'MyCustomPaginationList--subclass',
    ],
  },
});

Templates # A

first #
type: string|function
Optional

The template for the first page.

1
2
3
4
5
6
instantsearch.widgets.pagination({
  // ...
  templates: {
    first: '«',
  },
});
previous #
type: string|function
Optional

The template for the previous page.

1
2
3
4
5
6
instantsearch.widgets.pagination({
  // ...
  templates: {
    previous: '',
  },
});
next #
type: string|function
Optional

The template for the next page.

1
2
3
4
5
6
instantsearch.widgets.pagination({
  // ...
  templates: {
    next: '',
  },
});
last #
type: string|function
Optional

The template for the last page.

1
2
3
4
5
6
instantsearch.widgets.pagination({
  // ...
  templates: {
    last: '»',
  },
});

HTML output# A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div class="ais-Pagination">
  <ul class="ais-Pagination-list">
    <li class="ais-Pagination-item ais-Pagination-item--firstPage ais-Pagination-item--disabled">
      <span class="ais-Pagination-link" aria-label="First">‹‹</span>
    </li>
    <li class="ais-Pagination-item ais-Pagination-item--previousPage ais-Pagination-item--disabled">
      <span class="ais-Pagination-link" aria-label="Previous"></span>
    </li>
    <li class="ais-Pagination-item ais-Pagination-item--selected">
      <a class="ais-Pagination-link" href="#">1</a>
    </li>
    <li class="ais-Pagination-item ais-Pagination-item--page">
      <a class="ais-Pagination-link" href="#">2</a>
    </li>
    <li class="ais-Pagination-item ais-Pagination-item--page">
      <a class="ais-Pagination-link" href="#">3</a>
    </li>
    <li class="ais-Pagination-item">
      <a class="ais-Pagination-link" href="#">4</a>
    </li>
    <li class="ais-Pagination-item ais-Pagination-item--nextPage">
      <a class="ais-Pagination-link" aria-label="Next" href="#"></a>
    </li>
    <li class="ais-Pagination-item ais-Pagination-item--lastPage">
      <a class="ais-Pagination-link" aria-label="Last" href="#">››</a>
    </li>
  </ul>
</div>

Customize the UI with connectPagination# A

If you want to create your own UI of the pagination widget, you can use connectors.

It’s a 3-step process:

// 1. Create a render function
const renderPagination = (renderOptions, isFirstRender) => {
  // Rendering logic
};

// 2. Create the custom widget
const customPagination = instantsearch.connectors.connectPagination(
  renderPagination
);

// 3. Instantiate
search.addWidgets([
  customPagination({
    // instance params
  })
]);

Create a render function#

This rendering function is called before the first search (init lifecycle step) and each time results come back from Algolia (render lifecycle step).

const renderPagination = (renderOptions, isFirstRender) => {
  const {
    number[] pages,
    number currentRefinement,
    number nbHits,
    number nbPages,
    boolean isFirstPage,
    boolean isLastPage,
    boolean canRefine,
    function refine,
    function createURL,
    function widgetParams,
  } = renderOptions;

  if (isFirstRender) {
    // Do some initial rendering and bind events
  }

  // Render the widget
}

If SEO is critical to your search page, your custom HTML markup needs to be parsable:

  • use plain <a> tags with href attributes for search engines bots to follow them,
  • use semantic markup with structured data when relevant, and test it.

Refer to our SEO checklist for building SEO-ready search experiences.

Rendering options #

pages #
type: number[]

The pages relevant to the current situation and padding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const renderPagination = (renderOptions, isFirstRender) => {
  const { pages } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <ul>
      ${pages
        .map(
          page => `
            <li>
              <a href="#">${page + 1}</a>
            </li>
          `
        )
        .join('')}
    </ul>
  `;
};
currentRefinement #
type: number

The number of the page currently displayed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const renderPagination = (renderOptions, isFirstRender) => {
  const { pages, currentRefinement } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <ul>
      ${pages
        .map(
          page => `
            <li>
              <a
                href="#"
                style="font-weight: ${currentRefinement === page ? 'bold' : ''}"
              >
                ${page + 1}
              </a>
            </li>
          `
        )
        .join('')}
    </ul>
  `;
};
nbHits #
type: number

The number of hits computed for the last query (can be approximate).

1
2
3
4
5
6
7
8
9
const renderPagination = (renderOptions, isFirstRender) => {
  const { currentRefinement, nbPages, nbHits } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <span>
      ${currentRefinement + 1} of ${nbPages} page(s) for ${nbHits} hit(s)
    </span>
  `;
};
nbPages #
type: number

The number of pages for the result set.

1
2
3
4
5
6
7
8
9
const renderPagination = (renderOptions, isFirstRender) => {
  const { currentRefinement, nbPages, nbHits } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <span>
      ${currentRefinement + 1} of ${nbPages} page(s) for ${nbHits} hit(s)
    </span>
  `;
};
isFirstPage #
type: boolean

Whether the current page is the first page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const renderPagination = (renderOptions, isFirstRender) => {
  const { pages, isFirstPage, isLastPage } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <ul>
      ${
        !isFirstPage
          ? `
            <li>
              <a href="#">Previous</a>
            </li>
            `
          : ''
      }
      ${pages
        .map(
          page => `
            <li>
              <a href="#">
                ${page + 1}
              </a>
            </li>
          `
        )
        .join('')}
      ${
        !isLastPage
          ? `
            <li>
              <a href="#">Next</a>
            </li>
            `
          : ''
      }
    </ul>
  `;
};
isLastPage #
type: boolean

Whether the current page is the last page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const renderPagination = (renderOptions, isFirstRender) => {
  const { pages, isFirstPage, isLastPage } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <ul>
      ${
        !isFirstPage
          ? `
            <li>
              <a href="#">Previous</a>
            </li>
            `
          : ''
      }
      ${pages
        .map(
          page => `
            <li>
              <a href="#">
                ${page + 1}
              </a>
            </li>
          `
        )
        .join('')}
      ${
        !isLastPage
          ? `
            <li>
              <a href="#">Next</a>
            </li>
            `
          : ''
      }
    </ul>
  `;
};
canRefine #
type: boolean
Required

Indicates if search state can be refined.

1
2
3
4
5
6
7
8
const renderPagination = (renderOptions, isFirstRender) => {
  const { canRefine } = renderOptions;

  if (!canRefine) {
    document.querySelector('#pagination').innerHTML = '';
    return;
  }
};
refine #
type: function

Sets the current page and triggers a search.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const renderPagination = (renderOptions, isFirstRender) => {
  const { pages, currentRefinement, refine } = renderOptions;

  const container = document.querySelector('#pagination');

  container.innerHTML = `
    <ul>
      ${pages
        .map(
          page => `
            <li>
              <a
                href="#"
                data-value="${page}"
                style="font-weight: ${currentRefinement === page ? 'bold' : ''}"
              >
                ${page + 1}
              </a>
            </li>
          `
        )
        .join('')}
    </ul>
  `;

  [...container.querySelectorAll('a')].forEach(element => {
    element.addEventListener('click', event => {
      event.preventDefault();
      refine(event.currentTarget.dataset.value);
    });
  });
};
createURL #
type: function

Generates a URL for the next state. The number is the page to generate the URL for.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const renderPagination = (renderOptions, isFirstRender) => {
  const { pages, createURL } = renderOptions;

  document.querySelector('#pagination').innerHTML = `
    <ul>
      ${pages
        .map(
          page => `
            <li>
              <a href="${createURL(page)}">
                ${page + 1}
              </a>
            </li>
          `
        )
        .join('')}
    </ul>
  `;
};
widgetParams #
type: function

All original widget options forwarded to the render function.

1
2
3
4
5
6
7
8
9
10
11
12
13
const renderPagination = (renderOptions, isFirstRender) => {
  const { widgetParams } = renderOptions;

  widgetParams.container.innerHTML = '...';
};

// ...

search.addWidgets([
  customPagination({
    container: document.querySelector('#pagination'),
  })
]);

Create and instantiate the custom widget#

We first create custom widgets from our rendering function, then we instantiate them. When doing that, there are two types of parameters you can give:

  • Instance parameters: they are predefined parameters that you can use to configure the behavior of Algolia.
  • Your own parameters: to make the custom widget generic.

Both instance and custom parameters are available in connector.widgetParams, inside the renderFunction.

const customPagination = instantsearch.connectors.connectPagination(
  renderPagination
);

search.addWidgets([
  customPagination({
    totalPages: number,
    padding: number,
  })
]);

Instance options #

totalPages #
type: number
Optional

The total number of pages to browse.

1
2
3
customPagination({
  totalPages: 4,
});
padding #
type: number
default: 3
Optional

The padding of pages to show around the current page

1
2
3
customPagination({
  padding: 2,
});

Full example#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Create the render function
const renderPagination = (renderOptions, isFirstRender) => {
  const {
    pages,
    currentRefinement,
    nbPages,
    isFirstPage,
    isLastPage,
    refine,
    createURL,
  } = renderOptions;

  const container = document.querySelector('#pagination');

  container.innerHTML = `
    <ul>
      ${
        !isFirstPage
          ? `
            <li>
              <a
                href="${createURL(0)}"
                data-value="${0}"
              >
                First
              </a>
            </li>
            <li>
              <a
                href="${createURL(currentRefinement - 1)}"
                data-value="${currentRefinement - 1}"
              >
                Previous
              </a>
            </li>
            `
          : ''
      }
      ${pages
        .map(
          page => `
            <li>
              <a
                href="${createURL(page)}"
                data-value="${page}"
                style="font-weight: ${currentRefinement === page ? 'bold' : ''}"
              >
                ${page + 1}
              </a>
            </li>
          `
        )
        .join('')}
        ${
          !isLastPage
            ? `
              <li>
                <a
                  href="${createURL(currentRefinement + 1)}"
                  data-value="${currentRefinement + 1}"
                >
                  Next
                </a>
              </li>
              <li>
                <a
                  href="${createURL(nbPages - 1)}"
                  data-value="${nbPages - 1}"
                >
                  Last
                </a>
              </li>
              `
            : ''
        }
    </ul>
  `;

  [...container.querySelectorAll('a')].forEach(element => {
    element.addEventListener('click', event => {
      event.preventDefault();
      refine(event.currentTarget.dataset.value);
    });
  });
};

// Create the custom widget
const customPagination = instantsearch.connectors.connectPagination(
  renderPagination
);

// Instantiate the custom widget
search.addWidgets([
  customPagination({
    container: document.querySelector('#pagination'),
  })
]);
Did you find this page helpful?