Chapter 6 - Load more Past Events
It loads the next 6 past events each time the user clicks “Load more.”
“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 last 6 past events.
- The returned resources:
- Are rendered through an internal endpoint (
/api/v1/render-content-events/) - 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 Past Events module we define:
- The list container (js—past-events-list)
- The “Load more” container (js—scroll-load-more)
- The button (js—load-more)
<div class="f--container js--past-events-list"> ... <div class="js--results js--scroll-load-more"></div> <div class="f--col-12 u--display-flex u--justify-content-center"> <button class="c--btn-a js--load-more">{translations.load_more_text.text}</button> </div></div>Define GROQ
Section titled “Define GROQ”We define the GROQ query on the initial page load.
It fetches the first 6 past events, ordered by date in descending order (newest to oldest).
_type == 'past_events' => { ... 'events': *[_type == 'events' && date < now()] | order(date desc)[0..5]{ //the first 6 past events ${resource({ language, isReference: false })} }, 'translations': *[_type == 'global_texts'][0]{ 'load_more_text': *[_type == 'global_texts'][0]{'text' : past_events_load_more.${language} }, } }[0..5] = 6 items. Ordered by date descending.
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 loadMorePastEvents() { try { var query = `*[_type == 'events' && date < now()] | order(date desc)[${this.start}..${this.end}]{ 'booth_number': booth, 'typeName': *[_type == 'post_type_names'][0]{'title' : events.${this.language} }, 'is_external': boolean, boolean => { "external_url": external_link }, 'event_type': reference->title.${this.language}, "_type": _type, "title": title.${this.language}, 'location' : location.${this.language}, 'date': date, is_multiday => {end_date }, 'event_type': reference->title.${this.language}, 'total': count(*[_type == 'events' && date < now() ]) }`; const { result } = await sanityClient.fetch(query, {}, { filterResponse: false, }); this.total = result && result.length > 0 ? result[0].total : 0;
const cards = result ?? []; } }Render the result and apply to the HTML/Astro
Section titled “ Render the result and apply to the HTML/Astro” async loadMorePastEvents() { ... let resultContent = ""; try { const res = await fetch('/api/v1/render-content-events/', { 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 en fetch render-content:", err); } }The “Load more” pattern on Events page works as follows:
- Astro renders the first 6 events 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.
