<p>Here's a cut-down version of what I'm using on <a href="https://cld.agency/showcase" rel="nofollow noreferrer">https://cld.agency/showcase</a></p>
<p>The quick example below uses jQuery, but I have a more complete updated version using vanilla JS here: <a href="https://github.com/cld-agency/craftwind-boilerplate/blob/craft-4/templates/_listings/default.twig" rel="nofollow noreferrer">https://github.com/cld-agency/craftwind-boilerplate/blob/craft-4/templates/_listings/default.twig</a></p>
<pre><code>{% set theLayout = craft.app.request.isAjax and not craft.app.request.isLivePreview ? '_ajaxLayout' : '_layout' %}
{% extends theLayout %}

{% set entries = craft.entries.section('news').limit(12) %}
{% paginate entries as pageInfo, pageEntries %}

{# =============================================================================== #}

{% block content %}     
    &lt;h1&gt;News&lt;/h1&gt;
    &lt;p&gt;Introduction&lt;/p&gt;
    &lt;ul class=&quot;js-ajaxPagination&quot;&gt;      
        {% for card in pageEntries %}
            &lt;li&gt;{% include '_partials/card'  %}&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
    {# allow no-js/googlebot fallback with a regular href to the next page... #}
    &lt;p&gt;&lt;a class=&quot;js-loadMore&quot; href=&quot;{{ '/' ~ craft.app.request.pathInfo ~ '/p' ~ (pageInfo.currentPage + 1) }}&quot;&gt;Load more&lt;/a&gt;&lt;/p&gt;
{% endblock %}

{# =============================================================================== #}

{% block ajaxContent %}
    {% for card in pageEntries %}
        &lt;li&gt;{% include '_partials/card' %}&lt;/li&gt;
    {% endfor %}
{% endblock %}

{# ======================================
AJAX PAGINATION SCRIPT
========================================= #}

{% if pageInfo is defined and pageInfo.total &gt; 12 %}
    {% js %}
        var currentPage = 1;
        var perPage = 12;
        var count = perPage;
        $('.js-loadMore').click(function(e){
            e.preventDefault();
            if({{ pageInfo.total }} &gt; count){
                $.ajax({
                    url: $(this).attr('href'),
                }).done(function(data) {
                    var $new = $('.js-ajaxPagination').append(data);
                    var $button = $('.js-loadMore');
                    // increment the counters
                    count = count + perPage;
                    currentPage++;
                    $button.show().attr('href', '/{{ craft.app.request.pathInfo }}/p' + (currentPage + 1));
                    if({{ pageInfo.total }} &lt;= count){
                        $button.remove();
                    }
                });
            }
        });
    {% endjs %}
{% endif %}
</code></pre>
<p>So the trick is forking which template gets extended depending on whether it's an ajax request. Your _ajaxLayout template would be as simple as just:</p>
<p><code>{{ block('ajaxContent') }}</code></p>

CC BY-SA 4.0
2020-07-07T13:23:54.673Z
Thanks for the answer, looks great on your site! I'm having a bit of trouble getting it to work on my end. Would it be possible to see where my code would plug into this system? (Sorry, still learning :) )
Justin Kendall
CC BY-SA 4.0
2020-07-07T13:35:06.780Z
It's impossible to say without knowing your exact setup, and there are lots of ways of achieving the same result... My example assumes that you have a global layout template called `_layout` and that that template contains a  `{{ endBody() }}` tag to accept the injected js and a `content` block to output the content.
James Smith
CC BY-SA 4.0
2020-07-09T13:29:31.940Z
Ok, I'm running into some snags. I don't think I have the structure for this set up correctly. My site is currently set up with my header/footer in a file called `_main`  then all of my pages extends `_main` via `{% block content%}`. I'm currently trying to do this "More Posts" area in its own file `_loadMore` which I then `include` in my page. Right now it is doing some weird stuff like having multiple buttons and messing up the layout. And when I click the button, it literally duplicates the entire page in the area. So, I'm a bit confused on how you're using `theLayout` with your ajax
Justin Kendall
CC BY-SA 4.0
2020-07-09T13:39:44.337Z
Ok, I just realized that the funky layout stuff, was an error on my part. But it is still duplicating the page when I click my "Load More" button
Justin Kendall
CC BY-SA 4.0
2020-07-09T13:51:35.060Z
What is in your ajaxLayout template? (or whatever you named it)
James Smith
CC BY-SA 4.0
2020-07-09T13:53:34.627Z
`{{ block('ajaxContent') }}` like you said. but that is one thing I'm confused about. How are you linking to it/where do I put it?
Justin Kendall
CC BY-SA 4.0
2020-07-09T14:34:33.680Z
So your listings template extends either your main layout template (yours is called _main), or this ajaxLayout template depending on whether it's an ajax request (see the very first line in my example). If it is an ajax request, it'll output the block called 'ajaxContent', if not it'll output the block called 'content'.
James Smith
CC BY-SA 4.0
2020-07-09T15:03:40.673Z
Ok, I just edited my question to include the code I have utilizing your method. It is now giving me an internal server error.
Justin Kendall
CC BY-SA 4.0
2020-07-09T15:14:04.797Z
Right I see - you're trying to `extend` in an include, and that's never going to work... you need to get all this stuff into your `_page` template. It needs: a dynamic `extends` statement, a `content` block, and an `ajaxContent` block. If you want to abstract to an include, do it after the basic version is working.
James Smith
CC BY-SA 4.0
2020-07-09T15:18:32.487Z
Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/110408/discussion-between-a-b-creative-and-james-smith).
Justin Kendall
CC BY-SA 4.0
2020-07-07T12:40:08.947Z
2023-02-24T09:11:54.090Z
2023-02-24T09:11:54.090Z
James Smith
<p>The <a href="https://putyourlightson.com/plugins/sprig" rel="noreferrer">Sprig plugin</a> will save you a lot of trouble, and dynamically loading more entries is a perfect use-case for it. If you prefer to use jQuery, then by all means do so, but keep in mind that it is just as much of a dependency as Sprig. For the record, Sprig has been widely adopted and at the time of writing has over 4,700 active installs in the <a href="https://plugins.craftcms.com/sprig" rel="noreferrer">plugin store</a>.</p>
<p>Here is the <a href="https://putyourlightson.com/sprig/cookbook#load-more" rel="noreferrer">load more recipe</a> component, adapted for your markup.</p>
<pre><code>{% set limit = 10 %}
{% set offset = offset ?? 0 %}

{% set entryQuery = craft.entries.section('aBetterPost').offset(offset).limit(limit) %}
{% set entries = entryQuery.all() %}

{% for entry in entries %}
    &lt;a href=&quot;{{ entry.getUrl }}&quot; class=&quot;more-post&quot;&gt;
        &lt;div class=&quot;more-post-img-wrap&quot;&gt;
            {% set featuredImage = entry.featuredImage.one() %}
            &lt;img src=&quot;{{ featuredImage.getUrl }}&quot; class=&quot;more-post-img&quot;&gt;
            &lt;div class=&quot;red-overlay&quot;&gt;&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;more-post-content&quot;&gt;
            &lt;div class=&quot;more-post-title&quot;&gt;
                &lt;h3&gt;{{ entry.title }}&lt;/h3&gt;
            &lt;/div&gt;
            &lt;div class=&quot;next-icon&quot;&gt;
                &lt;i class=&quot;fas fa-chevron-right&quot;&gt;&lt;/i&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/a&gt;
{% endfor %}

{# If the total entry count is greater than the number that has been displayed #}
{% if entryQuery.count() &gt; offset + entries|length %}
    &lt;div id=&quot;more-posts&quot;&gt;
        &lt;div class=&quot;load-button-wrap&quot;&gt;
            &lt;a sprig s-val:offset=&quot;{{ offset + limit }}&quot; s-target=&quot;#more-posts&quot; s-swap=&quot;outerHTML&quot; href=&quot;#&quot;&gt;
                &lt;div class=&quot;load-button&quot;&gt;
                    &lt;p&gt;Load More&lt;/p&gt;
                &lt;/div&gt;
            &lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;
{% endif %}
</code></pre>

CC BY-SA 4.0
2022-02-08T15:54:46.313Z
Thanks for the answer! I had started using Sprig shortly after this question was posted, and I have loved it! Great plugin!
Justin Kendall
CC BY-SA 4.0
2021-12-26T10:28:09.277Z
2021-12-26T10:28:09.277Z
Ben Croker
<p>I am trying to make my entry loop have a load more button, and not sure how to do it. I tried the method from <a href="http://craftsnippets.com/articles/infinite-scrolling-and-lazy-loading-with-craft-cms" rel="nofollow noreferrer">http://craftsnippets.com/articles/infinite-scrolling-and-lazy-loading-with-craft-cms</a> but I could not get it to work.</p>
<p>Below is the code for my entry loop and &quot;Load More&quot; button. What would be the easiest way to make the &quot;Load More&quot; button functional?</p>
<pre><code>            &lt;div class=&quot;more-posts-wrap&quot;&gt;

                {% set moreABetterPost = craft.entries.section('aBetterPost').all() %}
                {% for entry in moreABetterPost %}
                &lt;a href=&quot;{{ entry.getUrl }}&quot; class=&quot;more-post&quot;&gt;
                    &lt;div class=&quot;more-post-img-wrap&quot;&gt;
                        {% set featuredImage = entry.featuredImage.one() %}
                        &lt;img src=&quot;{{ featuredImage.getUrl }}&quot; class=&quot;more-post-img&quot;&gt;
                        &lt;div class=&quot;red-overlay&quot;&gt;&lt;/div&gt;
                    &lt;/div&gt;
                    &lt;div class=&quot;more-post-content&quot;&gt;
                        &lt;div class=&quot;more-post-title&quot;&gt;
                            &lt;h3&gt;{{ entry.title }}&lt;/h3&gt;
                        &lt;/div&gt;
                        &lt;div class=&quot;next-icon&quot;&gt;
                            &lt;i class=&quot;fas fa-chevron-right&quot;&gt;&lt;/i&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/a&gt;
                {% endfor %}

            &lt;/div&gt;

            &lt;div class=&quot;load-button-wrap&quot;&gt;
                &lt;a href=&quot;#&quot;&gt;
                    &lt;div class=&quot;load-button&quot;&gt;
                        &lt;p&gt;Load More&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/a&gt;
            &lt;/div&gt;
</code></pre>
<p><strong>EDIT:</strong> Clarifying the structure of my site.</p>
<pre><code>{# _main.twig #}
Header Stuff
Header Stuff
...
{% block content %}
{# All of my pages exist in here by extending _main.twig #}
Page Stuff
Page Stuff
Page Stuff
...
LOAD MORE ENTRIES AREA
{% endblock %}
Footer Stuff
Footer Stuff
Footer Stuff
...
{# End of _main.twig #}
</code></pre>
<p><strong>EDIT 2:</strong> Showing the code that's not working in regards to James Smith's Answer.</p>
<p><strong>_loadMore.twig</strong></p>
<pre><code>{% set theLayout = craft.app.request.isAjax and not craft.app.request.isLivePreview ? '_ajaxLayout' : '_main' %}
{% extends theLayout %}

&lt;section&gt;
    &lt;div class=&quot;more-posts-section&quot;&gt;
        &lt;div class=&quot;section-header&quot;&gt;
            &lt;h2&gt;More Posts in &lt;span&gt;{{ category.title }}&lt;/span&gt;&lt;/h2&gt;
        &lt;/div&gt;

        {% set entries = craft.entries.section('aBetterPost').limit(6) %}
        {% paginate entries as pageInfo, pageEntries %}


        {% block content %}
        &lt;div class=&quot;more-posts-wrap-border&quot;&gt;
            &lt;div class=&quot;more-posts-wrap js-ajaxPagination&quot;&gt;
                {% for entry in aBetterPost%}
                  {% include '_layouts/_post' %}
                {% endfor %}
            &lt;/div&gt;
        &lt;/div&gt;
        {% endblock %}
        
        &lt;div class=&quot;load-button-wrap&quot;&gt;
            &lt;a href=&quot;{{ '/' ~ craft.app.request.pathInfo ~ '/p' ~ (pageInfo.currentPage + 1) }}&quot; class=&quot;load-button js-loadMore&quot;&gt;{{'load more'|t}}&lt;/a&gt;
        &lt;/div&gt;

        {% block ajaxContent %}
            {% for entry in aBetterPost%}
              {% include '_layouts/_post' %}
            {% endfor %}
        {% endblock %}
    &lt;/div&gt;
&lt;/section&gt;

{# ======================================
AJAX PAGINATION SCRIPT
========================================= #}

{% if pageInfo is defined and pageInfo.total &gt; 6 %}
    {% js %}
        var currentPage = 1;
        var perPage = 6;
        var count = perPage;
        $('.js-loadMore').click(function(e){
            e.preventDefault();
            if({{ pageInfo.total }} &gt; count){
                $.ajax({
                    url: $(this).attr('href'),
                }).done(function(data) {
                    var $new = $('.js-ajaxPagination').append(data);
                    var $button = $('.js-loadMore');
                    // increment the counters
                    count = count + perPage;
                    currentPage++;
                    $button.show().attr('href', '/{{ craft.app.request.pathInfo }}/p' + (currentPage + 1));
                    if({{ pageInfo.total }} &lt;= count){
                        $button.remove();
                    }
                });
            }
        });
    {% endjs %}
{% endif %}
</code></pre>
<p><strong>_page.twig</strong></p>
<pre><code>{% extends &quot;_layouts/_main&quot; %}
{% block content %}
   {# Other Page Stuff#}
   {# Other Page Stuff#}
   {% include '_layouts/_loadMore' %}
{% endblock %}
</code></pre>

CC BY-SA 4.0
2020-07-07T01:04:38.587Z
Have you set up element api endpoint as described in article and added js code to website that handle ajax requests? Also, alternative to using solution described in my article, would be using sprig plugin: https://putyourlightson.com/sprig-cookbook#load-more
Piotr Pogorzelski
CC BY-SA 4.0
2020-07-07T09:22:05.320Z
Why bother with the added complexity of using the Element API or adding and learning yet another abstraction like Sprig? Just call up your same listing template and have two Twig blocks - one for Ajax requests and one for regular requests. Keep it simple!
James Smith
CC BY-SA 4.0
2020-07-07T12:19:14.830Z
@piotrpog I added the JS, but I think the API stuff is where I got hung up. That Sprig plugin might be a good option, but I'm always a bit leery about non-first-party plugins with keeping up with Craft updates and such.
Justin Kendall
CC BY-SA 4.0
2020-07-07T12:21:14.487Z
@JamesSmith I am definitely not married to the method that I tried. If there is an easier/better way to do it, I'm all ears! Could you explain the process you mentioned a little more?
Justin Kendall
<craft3><ajax><forloop><javascript>
Load More Entries Button
# Load More Entries Button

(Asked by: username_0 on 2020-07-06)

I am trying to make my entry loop have a load more button, and not sure how to do it. I tried the method from http://craftsnippets.com/articles/infinite-scrolling-and-lazy-loading-with-craft-cms but I could not get it to work.
Below is the code for my entry loop and "Load More" button. What would be the easiest way to make the "Load More" button functional?
            

                {% set moreABetterPost = craft.entries.section('aBetterPost').all() %}
                {% for entry in moreABetterPost %}
                
                    
                        {% set featuredImage = entry.featuredImage.one() %}
                        
                        
                    
                    
                        
                            {{ entry.title }}
                        
                        
                            
                        
                    
                
                {% endfor %}

            

            
                
                    
                        Load More
                    
                
            

EDIT: Clarifying the structure of my site.
{# _main.twig #}
Header Stuff
Header Stuff
...
{% block content %}
{# All of my pages exist in here by extending _main.twig #}
Page Stuff
Page Stuff
Page Stuff
...
LOAD MORE ENTRIES AREA
{% endblock %}
Footer Stuff
Footer Stuff
Footer Stuff
...
{# End of _main.twig #}

EDIT 2: Showing the code that's not working in regards to James Smith's Answer.
_loadMore.twig
{% set theLayout = craft.app.request.isAjax and not craft.app.request.isLivePreview ? '_ajaxLayout' : '_main' %}
{% extends theLayout %}


    
        
            More Posts in {{ category.title }}
        

        {% set entries = craft.entries.section('aBetterPost').limit(6) %}
        {% paginate entries as pageInfo, pageEntries %}


        {% block content %}
        
            
                {% for entry in aBetterPost%}
                  {% include '_layouts/_post' %}
                {% endfor %}
            
        
        {% endblock %}
        
        
            {{'load more'|t}}
        

        {% block ajaxContent %}
            {% for entry in aBetterPost%}
              {% include '_layouts/_post' %}
            {% endfor %}
        {% endblock %}
    


{# ======================================
AJAX PAGINATION SCRIPT
========================================= #}

{% if pageInfo is defined and pageInfo.total > 6 %}
    {% js %}
        var currentPage = 1;
        var perPage = 6;
        var count = perPage;
        $('.js-loadMore').click(function(e){
            e.preventDefault();
            if({{ pageInfo.total }} > count){
                $.ajax({
                    url: $(this).attr('href'),
                }).done(function(data) {
                    var $new = $('.js-ajaxPagination').append(data);
                    var $button = $('.js-loadMore');
                    // increment the counters
                    count = count + perPage;
                    currentPage++;
                    $button.show().attr('href', '/{{ craft.app.request.pathInfo }}/p' + (currentPage + 1));
                    if({{ pageInfo.total }} <= count){
                        $button.remove();
                    }
                });
            }
        });
    {% endjs %}
{% endif %}

_page.twig
{% extends "_layouts/_main" %}
{% block content %}
   {# Other Page Stuff#}
   {# Other Page Stuff#}
   {% include '_layouts/_loadMore' %}
{% endblock %}

**Tags:** craft3, ajax, forloop, javascript
> **Comments:**
> **username_1**: Have you set up element api endpoint as described in article and added js code to website that handle ajax requests? Also, alternative to using solution described in my article, would be using sprig plugin: https://putyourlightson.com/sprig-cookbook#load-more  
> **username_2**: Why bother with the added complexity of using the Element API or adding and learning yet another abstraction like Sprig? Just call up your same listing template and have two Twig blocks - one for Ajax requests and one for regular requests. Keep it simple!  
> **username_0**: @piotrpog I added the JS, but I think the API stuff is where I got hung up. That Sprig plugin might be a good option, but I'm always a bit leery about non-first-party plugins with keeping up with Craft updates and such.  
> **username_0**: @JamesSmith I am definitely not married to the method that I tried. If there is an easier/better way to do it, I'm all ears! Could you explain the process you mentioned a little more?  

---

## 2 answers

### Answer by username_3 on 2021-12-26. Score: 6 | Accepted answer

The Sprig plugin will save you a lot of trouble, and dynamically loading more entries is a perfect use-case for it. If you prefer to use jQuery, then by all means do so, but keep in mind that it is just as much of a dependency as Sprig. For the record, Sprig has been widely adopted and at the time of writing has over 4,700 active installs in the plugin store.
Here is the load more recipe component, adapted for your markup.
{% set limit = 10 %}
{% set offset = offset ?? 0 %}

{% set entryQuery = craft.entries.section('aBetterPost').offset(offset).limit(limit) %}
{% set entries = entryQuery.all() %}

{% for entry in entries %}
    
        
            {% set featuredImage = entry.featuredImage.one() %}
            
            
        
        
            
                {{ entry.title }}
            
            
                
            
        
    
{% endfor %}

{# If the total entry count is greater than the number that has been displayed #}
{% if entryQuery.count() > offset + entries|length %}
    
        
            
                
                    Load More
                
            
        
    
{% endif %}

> **Comments:**
> **username_0**: Thanks for the answer! I had started using Sprig shortly after this question was posted, and I have loved it! Great plugin!  

---

### Answer by username_2 on 2020-07-07. Score: 4

Here's a cut-down version of what I'm using on https://cld.agency/showcase
The quick example below uses jQuery, but I have a more complete updated version using vanilla JS here: https://github.com/cld-agency/craftwind-boilerplate/blob/craft-4/templates/_listings/default.twig
{% set theLayout = craft.app.request.isAjax and not craft.app.request.isLivePreview ? '_ajaxLayout' : '_layout' %}
{% extends theLayout %}

{% set entries = craft.entries.section('news').limit(12) %}
{% paginate entries as pageInfo, pageEntries %}

{# =============================================================================== #}

{% block content %}     
    News
    Introduction
          
        {% for card in pageEntries %}
            {% include '_partials/card'  %}
        {% endfor %}
    
    {# allow no-js/googlebot fallback with a regular href to the next page... #}
    Load more
{% endblock %}

{# =============================================================================== #}

{% block ajaxContent %}
    {% for card in pageEntries %}
        {% include '_partials/card' %}
    {% endfor %}
{% endblock %}

{# ======================================
AJAX PAGINATION SCRIPT
========================================= #}

{% if pageInfo is defined and pageInfo.total > 12 %}
    {% js %}
        var currentPage = 1;
        var perPage = 12;
        var count = perPage;
        $('.js-loadMore').click(function(e){
            e.preventDefault();
            if({{ pageInfo.total }} > count){
                $.ajax({
                    url: $(this).attr('href'),
                }).done(function(data) {
                    var $new = $('.js-ajaxPagination').append(data);
                    var $button = $('.js-loadMore');
                    // increment the counters
                    count = count + perPage;
                    currentPage++;
                    $button.show().attr('href', '/{{ craft.app.request.pathInfo }}/p' + (currentPage + 1));
                    if({{ pageInfo.total }} <= count){
                        $button.remove();
                    }
                });
            }
        });
    {% endjs %}
{% endif %}

So the trick is forking which template gets extended depending on whether it's an ajax request. Your _ajaxLayout template would be as simple as just:
{{ block('ajaxContent') }}

> **Comments:**
> **username_0**: Thanks for the answer, looks great on your site! I'm having a bit of trouble getting it to work on my end. Would it be possible to see where my code would plug into this system? (Sorry, still learning :) )  
> **username_2**: It's impossible to say without knowing your exact setup, and there are lots of ways of achieving the same result... My example assumes that you have a global layout template called `_layout` and that that template contains a  `{{ endBody() }}` tag to accept the injected js and a `content` block to output the content.  
> **username_0**: Ok, I'm running into some snags. I don't think I have the structure for this set up correctly. My site is currently set up with my header/footer in a file called `_main`  then all of my pages extends `_main` via `{% block content%}`. I'm currently trying to do this "More Posts" area in its own file `_loadMore` which I then `include` in my page. Right now it is doing some weird stuff like having multiple buttons and messing up the layout. And when I click the button, it literally duplicates the entire page in the area. So, I'm a bit confused on how you're using `theLayout` with your ajax  
> **username_0**: Ok, I just realized that the funky layout stuff, was an error on my part. But it is still duplicating the page when I click my "Load More" button  
> **username_2**: What is in your ajaxLayout template? (or whatever you named it)  
> **username_0**: `{{ block('ajaxContent') }}` like you said. but that is one thing I'm confused about. How are you linking to it/where do I put it?  
> **username_2**: So your listings template extends either your main layout template (yours is called _main), or this ajaxLayout template depending on whether it's an ajax request (see the very first line in my example). If it is an ajax request, it'll output the block called 'ajaxContent', if not it'll output the block called 'content'.  
> **username_0**: Ok, I just edited my question to include the code I have utilizing your method. It is now giving me an internal server error.  
> **username_2**: Right I see - you're trying to `extend` in an include, and that's never going to work... you need to get all this stuff into your `_page` template. It needs: a dynamic `extends` statement, a `content` block, and an `ajaxContent` block. If you want to abstract to an include, do it after the basic version is working.  
> **username_0**: Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/110408/discussion-between-a-b-creative-and-james-smith).  

---

