Recommended Free Tools
The quickest way to add a Load More button is a plugin such as Ajax Load More. If the archive needs custom filters, markup, or request logic, build the button around the WordPress REST API or a server-side WP_Query. Whichever route you choose, request the next page, append the returned cards, preserve the archive’s query rules, and remove or disable the button when no posts remain.
Choose the implementation that fits your site
| Approach | Setup effort | Control | Best fit | Maintenance |
|---|---|---|---|---|
| Plugin | Lowest | Uses the plugin’s query and markup options | Editor-managed sites and standard archives | Plugin updates and compatibility checks |
| REST API plus JavaScript | Medium to high | Fine control over requests, filters, and rendering | Custom themes and interactive archives | Your JavaScript, templates, and endpoint assumptions |
| WP_Query with custom endpoint | High | Server-rendered WordPress markup and query logic | Themes that must reuse PHP card templates | Your PHP endpoint and front-end integration |
Use a plugin for the shortest setup
Install Ajax Load More from Plugins → Add New, then configure its documented shortcode or block. For example:
[ajax_load_more post_type="post, portfolio" posts_per_page="6" button_label="Load More"]
The listing says the shortcode can be placed in the content editor or a theme template and that a block-editor block is available. Review the current listing for supported WordPress versions, compatibility, and any paid add-ons before activating it.
Use the REST API for custom behavior
A custom button can request a page such as /wp-json/wp/v2/posts?page=2&per_page=6, render the returned posts into your card template, and then advance to page 3. This gives you control over the HTML and interactions, but you must write the rendering and state management yourself. The official pagination documentation covers page, per_page, and offset: WordPress REST API pagination.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
Use WP_Query when PHP should render the cards
For a theme-specific loop, create a secondary WP_Query, iterate with have_posts() and the_post(), and call wp_reset_postdata() afterward so the main query’s post context is restored. The PHP loop still needs an AJAX or REST endpoint and front-end button code to deliver later batches. See the WP_Query reference.
How the Load More request cycle works
- Render the archive’s initial batch and store the next page number in JavaScript.
- When the user activates the button, disable it and show a loading state.
- Request the next page with the same post type, category, search term, ordering, and other filters used for the initial archive.
- Render each returned post using the site’s card markup and append the cards to the existing list.
- Increment the page only after a successful response.
- Read
X-WP-TotalPages(or determine that the returned batch is empty) and remove or disable the button when the final page has been reached.
WordPress REST responses include X-WP-Total and X-WP-TotalPages headers for paginated collections. The per_page value accepts 1–100; WordPress Developer Resources warns, “Large queries can hurt site performance, so per_page is capped at 100 records.” That limit is not a performance benchmark or a universal recommendation. Start with a small batch and tune it after observing response time and the cost of rendering your cards.
Rank #2
Keep later requests identical to the archive
Every condition that shaped the first batch must be carried into subsequent requests. A category archive, custom post type, date order, taxonomy filter, search term, or meta query that is omitted on page 2 can produce duplicates or unrelated posts. In a REST request, express those conditions with the endpoint’s supported parameters; in WP_Query, keep the arguments consistent. This consistency is an implementation requirement, not an automatic WordPress guarantee.
Reference REST API implementation pattern
The following pattern shows the state transitions; adapt the selectors and card rendering to your theme.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #3
const button = document.querySelector('[data-load-more]');
const list = document.querySelector('[data-post-list]');
const status = document.querySelector('[data-load-status]');
let page = 2;
button.addEventListener('click', async () => {
button.disabled = true;
button.setAttribute('aria-busy', 'true');
status.textContent = 'Loading posts…';
try {
const response = await fetch(`/wp-json/wp/v2/posts?page=${page}&per_page=6`);
if (!response.ok) throw new Error('Request failed');
const posts = await response.json();
posts.forEach(post => {
// Create a card that matches the active theme, then append it.
list.insertAdjacentHTML('beforeend', renderPostCard(post));
});
page += 1;
const totalPages = Number(response.headers.get('X-WP-TotalPages'));
if (!posts.length || (totalPages && page > totalPages)) {
button.remove();
} else {
button.disabled = false;
}
status.textContent = `${posts.length} posts added.`;
} catch (error) {
status.textContent = 'Could not load posts. Try again.';
button.disabled = false;
} finally {
button.removeAttribute('aria-busy');
}
});
renderPostCard is site-specific: map the response fields to the exact title, link, excerpt, image, and metadata structure used by your archive. Keep filters in the URL, and do not increment page when a request fails.
Security and failure behavior
A public read-only posts request does not automatically require authentication. Choose the endpoint and exposed fields for the actual use case. If you use a custom admin-ajax handler or an authenticated endpoint, follow WordPress’s nonce and capability guidance for that context. The WordPress tutorial explains replacing an admin-AJAX example with the REST API and discusses nonce checking: Using the WordPress REST API.
Rank #4
- Disable the button during a request so rapid clicks cannot create concurrent duplicate batches.
- Show a visible loading state and restore the button after a network or server error.
- Handle an empty response and a final page without leaving a non-functional control.
- Log or surface errors during development, but give visitors a concise retry message.
Make the control accessible
Use a real <button type="button">, not a styled link or clickable div. Keep a visible focus indicator, expose a disabled or busy state while fetching, and provide a polite live-status region announcing that posts were added. Return focus to the button unless it has been removed; if focus moves into newly inserted content, make that change intentional and predictable. The Load More Ajax changelog describes button state attributes, a screen-reader live status region, and :focus-visible styling. Treat those as an implementation checklist, not evidence that every plugin or custom script meets accessibility requirements.
Test the button in the real site
- First click, second click, and the final available page.
- An archive with no additional results.
- Network failure, server error, and a retry after failure.
- Keyboard activation, visible focus, screen-reader announcements, and disabled-state feedback.
- Category, search, taxonomy, post-type, and ordering filters carried through every request.
- Card markup, images, excerpts, and scripts with the active theme and other plugins.
Generic code cannot guarantee compatibility with every theme’s card template or query setup, so test the complete archive rather than only the button in isolation.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

