Welcome back! In our last lesson, we successfully created a static partial for a hero banner, which renders data passed directly from our homepage.json file. We established a "dispatcher" pattern where our main index.html template calls the right partial for each section.
Today, we're taking a significant step forward. We'll move from static components to dynamic ones. The learning outcome for this lesson is to generate a reusable partial that displays a curated list of content from a specific site section (e.g., 'Latest Blog Posts'). This partial won't just display data it's given; it will actively fetch and display content from another part of your site, making your homepage a true, dynamic dashboard.
For you, as an SEO professional, this is a fundamental technique for improving site architecture. Sections like "Latest Articles," "Popular Guides," or "Related Services" are critical for user engagement and for distributing link equity throughout a site.
The Challenge: Escaping the Local Context
Our hero banner partial was simple: it received a chunk of data (the context) and displayed it. But how do we build a partial that shows the "3 Latest Blog Posts"? The information about those posts isn't in our homepage.json file; it lives in the content/blog/ directory.
The . (dot) in a Hugo template refers to the current context. When our index.html dispatcher calls a partial for a homepage section, the . is the data for that specific section. How can the partial "see" outside this context to find the blog posts?
This is a common challenge, and solving it is key to unlocking Hugo's power. The solution lies in using Hugo's global variables.

Step 1: Accessing Global Content with .Site.Pages
Hugo provides a global variable, .Site.Pages, which is an array of all pages on your entire website. By using .Site.Pages instead of .Pages or .Data.Pages (which are context-dependent), a partial can access any piece of content from anywhere on the site, regardless of where the partial is being rendered.
This forum discussion provides a perfect, real-world example of a developer facing this exact problem.
[SOLVED] How to use `range` to display partial on all pages?
This Hugo forum thread, '[SOLVED] How to use range to display partial on all pages?', directly addresses our challenge. A user wants to show latest blog posts, but it only works on some pages.
Please read through the user's question and the provided solution. Pay close attention to: The initial problem caused by using .Data.Pages. The solution using .Site.Pages to access all site content. The clarification on what .Data.Pages actually means (children of the current context).
As you saw in the forum thread, the key is to switch from a context-specific variable like .Data.Pages to the global .Site.Pages. This gives our partial the superpower to see the entire site.
Step 2: Filtering and Slicing the Content
Now that we can access all pages, we need to narrow them down. For a "Latest Blog Posts" section, we need to perform three steps:
- Filter the pages to only include those in the
blogsection. - Sort them by date (implicitly, Hugo's default sort is usually sufficient, but explicit sorting is also possible).
- Limit the list to a specific number, like the 3 most recent posts.
Hugo provides powerful template functions to do this in a single line. The main ones we'll use are where, first, and range.
range: This function iterates, or loops, over a list of items. We'll use it to create an<li>for each blog post.where: This function filters a list. We'll use it like this:where .Site.Pages "Section" "blog". This command takes the list of all site pages and returns only those where theSectionparameter is equal to "blog".first: This function slices a list, returning only the first N items. For example,first 3 ...will give us the three most recent posts.
To see the range function in action, you can review this quick video.
Functions | Hugo - Static Site Generator | Tutorial 18
The 'Functions' video from Giraffe Academy gives a good demonstration of the range function, which is essential for looping through our list of posts.
Please watch from 03:16 to 05:00. Notice how the range .Pages command iterates through a collection, and inside the loop, you can access properties of each page like .Title.
By combining these functions, we create a "pipeline." We get all the pages, filter them by section, take the first few, and then loop over that final list. The code from the forum post you read sums it up perfectly: {{ range first 10 (where .Site.Pages "Section" "blog") }}.
Step 3: Vibe Coding the "Latest Posts" Partial
Let's apply this knowledge. First, we need an entry in our data/homepage.json to tell the dispatcher to render this new partial.
data/homepage.json (new entry)
{
"id": "latest-news",
"type": "latest-posts",
"title": "Latest From The Blog",
"section": "blog",
"limit": 3
}
This data structure is smart. It not only defines a title but also makes the partial reusable by allowing us to specify which section to pull from and how many items to limit. You could easily create another section for "Latest News" by just changing the section value.
Now, let's write the prompt for our AI assistant.
Prompt for AI:
I need you to generate a Hugo partial for a "latest posts" section. The file should be named
latest-posts.htmland be located inlayouts/partials/homepage/.Context:
The partial will receive a data object (.) with the following fields:
.title: The heading for the section (e.g., "Latest From The Blog")..section: The content section to query (e.g., "blog")..limit: The maximum number of posts to display (e.g., 3).Instructions:
- Create an HTML
<section>element as the main container.- Display the partial's
.titlein an<h2>tag.- Use Hugo's template functions to get the desired pages. The logic should be:
firstN pages (.limit) from the result ofwhere .Site.RegularPages "Section" is .section.- Use the
rangefunction to loop through these pages.- Inside the loop, for each page, create a list item
<li>inside a<ul>.- Each list item should contain an
<a>link. The link'shrefmust be the page's.Permalinkand the link's text must be the page's.Title.- Below the title link, display the page's summary using
{{ .Summary }}.- Use sensible default values for
.limit(3) and.section("blog") in case they are not provided. Use CSS classes for styling.
Note: I've specified .Site.RegularPages instead of .Site.Pages. This is a best practice, as RegularPages automatically excludes section pages, taxonomies, and other non-content pages, giving you a cleaner list to start with.
Step 4: Analyzing the AI-Generated Code
Here’s a likely output from the AI, which we can now analyze.
layouts/partials/homepage/latest-posts.html
{{/*
This partial displays a list of the latest posts from a given section.
Context expects: .title, .section, .limit
*/}}
<section class="latest-posts-section" id="{{ .id | default "latest-posts" }}">
<div class="container">
{{ with .title }}
<h2 class="section-title">{{ . }}</h2>
{{ end }}
{{/* Define variables with default fallbacks */}}
{{ $limit := .limit | default 3 }}
{{ $section := .section | default "blog" }}
{{/* Find the pages to display */}}
{{ $pages_to_show := first $limit (where .Site.RegularPages "Section" $section) }}
{{ if $pages_to_show }}
<ul class="post-list">
{{ range $pages_to_show }}
<li class="post-item">
<h3 class="post-title"><a href="{{ .Permalink }}">{{ .Title }}</a></h3>
<p class="post-summary">{{ .Summary }}</p>
</li>
{{ end }}
</ul>
{{ else }}
<p>No posts found in the '{{ $section }}' section.</p>
{{ end }}
</div>
</section>
Code Breakdown:
- Variables with Defaults: The lines
{{ $limit := .limit | default 3 }}are excellent defensive programming. If our JSON doesn't specify a limit, it defaults to 3. - The Pipeline:
{{ $pages_to_show := first $limit (where .Site.RegularPages "Section" $section) }}is the heart of the partial. It perfectly executes our logic, storing the result in a variable for clarity. - The
rangeloop: The code iterates through$pages_to_show. Inside this loop, the context.now refers to the current page being processed, so we can access its properties like.Permalink,.Title, and.Summary. - Conditional Rendering: The
{{ if $pages_to_show }}...{{ else }}...{{ end }}block is another sign of quality code. It ensures the page doesn't break if no posts are found, and instead displays a helpful message.

Test your understanding!
Let's say your blog posts have a front matter parameter called author. How would you modify the <li> element inside the range loop to display the author's name for each post? For example, in a <span> tag.
Show answer
Inside the range loop, the context . refers to the page object. You can access its front matter parameters via .Params. So, you would add the following line inside the <li>:
<span class="post-author">By: {{ .Params.author }}</span>
It's also good practice to wrap this in a with block to ensure it only renders if the author parameter exists:
{{ with .Params.author }}<span class="post-author">By: {{ . }}</span>{{ end }}
Conclusion
Congratulations! You've just created your first dynamic content component. This partial is powerful because it's both reusable and aware of the site's overall content structure. You can now drop this partial anywhere on your site to feature content from any section, a technique at the heart of building complex, interconnected websites.
Key Takeaways:
- To access content from outside a partial's local context, use the global
.Site.RegularPagesvariable. - You can create powerful content queries by "pipelining" Hugo functions like
where,first, andrange. - Designing partials to accept parameters (like
sectionandlimit) makes them incredibly flexible and reusable. - Prompting an AI with clear logic (
where...first...range) and data requirements results in high-quality, robust template code.
Preview of the next lesson:
We have now built both a static partial (hero banner) and a dynamic one (latest posts). Our homepage is starting to feel like a real website. In the next module, we'll address another fundamental piece of the puzzle: navigation. We'll kick off Module 6 by learning how to define a multi-level site menu and write a prompt to generate a partial that renders it, allowing users to navigate the complex site we're building.