Horizon Is Now Shopify's Default Theme, and Its Blog Has No Real Pagination — Here's Why That's Costing You Search Traffic
Horizon replaced Dawn as the default theme for new Shopify stores in 2026, and its blog section ships with infinite scroll and nothing else — no page numbers, no ?page= URLs, no pagination setting like collections got. Merchants with more than 50 posts are finding older content effectively invisible to Google, because Googlebot can't scroll. Here's the audit and the Liquid fix.
If you launched your store on Horizon — Shopify's new default theme for stores created in 2026, replacing Dawn — and you've been publishing blog content for a while, there's a decent chance Google has quietly stopped seeing most of it. Not because of a penalty, and not because of thin content. Because the theme's blog template has no real pagination, and what it uses instead is invisible to a crawler.
This isn't a hypothetical. Shopify Community and developer-forum threads through the first half of 2026 describe the same pattern from unrelated stores: a merchant publishes their 51st, 100th, or 167th blog post, checks the blog index page, and finds only the most recent fifty. One thread on the Shopify Developer Community, reported against Horizon 3.4.0, states it plainly — "a max of 50 articles can be displayed" with "no pagination block or setting to control the number of articles per page." A separate, more recent thread on the main Shopify Community found the missing posts were technically still reachable: Horizon loads the first 50 articles, then fetches more via JavaScript as the visitor scrolls past them. So a human patient enough to keep scrolling can eventually see all 167 posts. Googlebot cannot.
Why "it loads on scroll" doesn't help your rankings
This is the part that trips people up, because from inside a browser the content genuinely is there — scroll far enough and it appears. But Google's own Search Central documentation on pagination and incremental page loading is direct about what its crawler will and won't do: "Google's crawlers don't 'click' buttons and generally don't trigger JavaScript functions that require user actions." An infinite-scroll fetch triggered by a scroll event is exactly that kind of interaction. Googlebot renders the initial page, sees the first batch of articles, and has no next action to take — there's no <a href="...page=2"> for it to follow, because Horizon's blog template doesn't render one.
Google's guidance is specific about what a crawlable version needs: "Give each page a unique URL. For example, include a ?page=n query parameter, as URLs in a paginated sequence are treated as separate pages by Google." It also warns against the mistake of canonicalizing every page back to page one — each page needs to "give each page its own canonical URL." Horizon's blog, in its default configuration, satisfies neither requirement. There's no second URL to give a unique canonical to, because there's no second URL at all.
The practical result: your 51st blog post onward can rank for exactly nothing, no matter how good the content is, because Google never discovers a URL that leads to it independent of the un-paginated index page. If that post is also missing from your sitemap (a common oversight once a blog passes the point where anyone checks it manually), it may never get crawled at all.
Why collections dodged this and blogs didn't
It's worth knowing this gap is specific to the blog template, not Horizon as a whole. Community threads on collection pagination describe the same infinite-scroll default shipping on collection pages in early Horizon versions — but merchants found a fix inside the theme editor: Online Store → Themes → Customize → Collection template exposes a checkbox that switches collections from infinite scroll to numbered pagination, added from theme version 3.0.1 onward. No such setting exists for the blog template as of Horizon 3.4.0. If you've only ever checked your collection pages for this problem and found a toggle waiting for you, that's why the blog looks different — it simply didn't get the same setting.
Diagnose your own store in five minutes
Don't assume this affects you just because you're on Horizon — confirm it directly.
- Count your published blog posts in Shopify admin → Content → Blog posts. If you're under 50, this doesn't apply to you yet.
- If you're over 50, view your blog index page's source as a crawler would, without executing JavaScript:
curl -s https://yourstore.com/blogs/news | grep -o 'href="[^"]*page='. An empty result means there's no server-rendered link to a second page — the exact condition Google's guidance warns about. - In Google Search Console, open the Pages report and search for your blog URL pattern (e.g.
/blogs/news/). Compare the count of indexed blog post URLs against your actual published count from admin. A gap that lines up with "everything after roughly post 50" confirms the theory rather than leaving it as a guess. - Check whether older posts are present in
/sitemap.xmlat all. If your sitemap only reflects what Shopify's own indexing sees, posts that never got a discoverable URL may be missing there too.
The fix: give the crawler a real URL without breaking infinite scroll for humans
The fix isn't to rip out infinite scroll — it's a decent UX pattern for a blog and shoppers are used to it. The fix is to make sure a real, linkable, paginated structure exists underneath it, exactly the "hybrid" model Google's own documentation recommends: a scroll-driven experience for visitors, with every scroll position corresponding to a crawlable URL.
In your theme editor, go to Edit code on a duplicated (test) theme and open sections/main-blog.liquid. Wrap the article loop in Liquid's paginate tag instead of relying on the default unpaginated loop, which silently caps at 50 items with no error:
{%- paginate blog.articles by 20 -%}
<div class="blog-posts-list" data-infinite-scroll="{{ section.settings.enable_infinite_scroll }}">
{%- for article in blog.articles -%}
{%- render 'article-card', article: article -%}
{%- endfor -%}
</div>
{%- if paginate.pages > 1 -%}
<nav class="pagination" role="navigation" aria-label="Pagination">
{%- if paginate.previous -%}
<a href="{{ paginate.previous.url }}" rel="prev">Newer posts</a>
{%- endif -%}
{%- for part in paginate.parts -%}
{%- if part.is_link -%}
<a href="{{ part.url }}">{{ part.title }}</a>
{%- else -%}
<span aria-current="page">{{ part.title }}</span>
{%- endif -%}
{%- endfor -%}
{%- if paginate.next -%}
<a href="{{ paginate.next.url }}" rel="next">Older posts</a>
{%- endif -%}
</nav>
{%- endif -%}
{%- endpaginate -%}
Three things make this work correctly for search rather than just adding a widget:
- The
<a href>tags render server-side, in the initial HTML response — not injected by the infinite-scroll JavaScript after load. That's what makes them discoverable by a crawler that doesn't execute scroll-triggered fetches. - Each paginated URL (
/blogs/news?page=2,/blogs/news?page=3) needs its own self-referencing canonical tag, not one pointing back to page one — check your theme'sSeo-equivalent output for the blog template and confirm it isn't hardcoding the canonical to the base/blogs/newsURL, which would undo the fix by telling Google to ignore every page but the first anyway. - Leave your existing infinite-scroll JavaScript in place for the on-page experience if you want it — it can still progressively enhance these same pages by pre-fetching
paginate.next.urland appending it to the DOM on scroll. The scroll behavior and the crawlable URL structure aren't mutually exclusive; Horizon's default just shipped only the first half of that pattern.
After deploying, resubmit your blog's paginated URLs (or the updated sitemap, once it reflects them) in Search Console and watch the Pages report over the following weeks — that's your confirmation the gap has actually closed, not just that the code compiles.
If you haven't hit 50 posts yet
Build the paginate block in now, while it's a ten-minute change instead of a retroactive fix competing with whatever else broke when your archive quietly went dark. The same "server-rendered link, not JS-only" principle applies broadly any time Shopify's default templates lean on scroll or click-triggered loading for content you actually want indexed — it's worth checking your filtered collection URLs for the same gap if you're running faceted navigation, since the failure mode is identical even though the trigger is different.
Also weigh the tradeoff Horizon's own community discussions raise honestly: rendering very large post counts server-side has a real page-weight cost, which is the same territory covered in our Core Web Vitals remediation checklist. Twenty items per paginated page, not 250, keeps this fix from trading an indexing problem for a performance one.
Code Kaarigari audits Shopify themes for exactly this class of gap — places where a default template's UX choice quietly makes content invisible to search — before it costs a store months of organic traffic on content that was already written and paid for. See our Shopify SEO and theme development services, or contact Code Kaarigari if you want your Horizon build checked for this specifically.
Sources reviewed
- Google Search Central: Pagination and incremental page loading
- Shopify Developer Community: Horizon 3.4.0 — max of 50 blog articles
- Shopify Community: How to display more than 50 blog articles (and collection items?)
- Shopify Community: Blog posts in Horizon
- Shopify Community: Horizon — pagination for collections?