Chapter 6 - Load more Authors
On the Author page, we display a list of all resources published by (or featuring) a given author.
When an author has more than 9 resources, a “Load more” button appears at the end of the list, allowing users to fetch additional results without reloading the page.
“Load more” Behavior
Section titled ““Load more” Behavior”- When the user clicks “Load more”:
- A JavaScript handler is triggered.
- A loading spinner is shown.
- The handler updates the pagination parameters:
start: first index of the next batchend: last index of the next batch
- A new request is sent to Sanity:
- Sanity returns the next 9 resources.
- The returned resources:
- Are rendered through an internal endpoint (
/api/v1/render-content-authors/) - Are appended dynamically to the DOM
- Are rendered through an internal endpoint (
- When there are no more results:
- The “Load more” button is hidden.
Define HTML/Astro classes and elements
Section titled “Define HTML/Astro classes and elements”Inside the author template (pages/[lang]/author/[slug].astro) we define:
- The list container
- The “Load more” container
- The button
- A hidden field containing the
authorId
<div class="f--container js--author-list"> ... <div class="f--row f--gap-a u--pt-10 u--pt-tablets-7 js--load-more-container"> <div class="f--col-12 u--display-flex u--justify-content-center"> <button class="c--btn-a js--load-more-resources"> {single.load_more_text.text} </button> </div>
<input type="hidden" class="js--author-id" value={single.id} /> </div></div>Note: the hidden input is required to build the GROQ query client-side and fetch more resources for the correct au> thor.
Define groq
Section titled “Define groq”Define the GROQ query on the initial page load.
"resources" : *[_type in ['blogs', 'webinars', 'podcasts','research'] && (author._ref == ^._id || ^._id in speakers[].speaker._ref || ^._id in authors[].author._ref)]| order(date desc)[0..8] { "type": _type, "card": {${resource({language, isReference: false})}} }[0..8] = 9 items. Ordered by date descending. Includes resources where the person appears as: author, speaker, authors[]
DOM References and Initial Pagination State (hitsPerPage, start, end, language)
Section titled “DOM References and Initial Pagination State (hitsPerPage, start, end, language)” this.DOM = { loadMoreContainer :document.querySelector(".js--load-more-container"), loadMoreBtn : document.querySelector(".js--load-more-resources"), authorID : document.querySelector(".js--author-id")?.value, resultsContainer : document.querySelector(".js--results"), }
// Settings this.language = getCookie("preferred_lang") || "en"; // Default to English this.hitsPerpage = 9; // Number of results per load this.start = this.hitsPerpage; this.end = this.hitsPerpage + this.start - 1; this.contentPosition = "beforebegin"; // Default insert position for new HTML this.emitter = payload.emitter;Call the Query to Sanity
Section titled “Call the Query to Sanity” async loadMoreAuthors() { ... const query = `*[_type == 'people' && _id == '${this.DOM.authorID}'][0]{ "resources": *[_type in ['blogs', 'webinars', 'podcasts','research'] && (author._ref == ^._id || ^._id in speakers[].speaker._ref || ^._id in authors[].author._ref)] | order(date desc) [${this.start} .. ${this.end}] { "type": _type, "card": {${resource({ language: this.language, isReference: false })}} }, "total": count(*[_type in ['blogs', 'webinars', 'podcasts','research'] && (author._ref == ^._id || ^._id in speakers[].speaker._ref || ^._id in authors[].author._ref)]) }`;
const { result } = await sanityClient.fetch(query, {}, { filterResponse: false }); this.total = result?.total || 0; const cards = result?.resources || []; ... }Render the result and apply to the HTML/Astro
Section titled “ Render the result and apply to the HTML/Astro” async loadMoreAuthors() { ... let resultContent = ""; try { const res = await fetch("/api/v1/render-content-authors/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: cards, language: this.language, resultsTotal: this.total, }), });
const renderResult = await res.json(); resultContent = renderResult.result || ""; } catch (err) { console.error("Error in fetch render-content:", err); } }The “Load more” pattern on Author pages works as follows:
- Astro renders the first 9 resources at build/load time.
- JS handles incremental pagination via GROQ
[start..end]. - Cards are rendered through an internal API.
- New items are appended to the DOM.
- The “Load more” button disappears once all resources are loaded.
