API Reference / InstantSearch.js Widgets / pagination
Aug. 23, 2019

pagination

Widget 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

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

The widget is limited to 1,000 hits, which is what the maximum the engine lets you browse.

Examples

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

Options

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

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

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 - connectPagination

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,
    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

Returns true if the current page is also 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

Returns true if the current page is also 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>
  `;
};
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?