How to Group Documents by Published Date with the Desk Tool Plugin

5 replies
Last updated: May 9, 2020
Hello 👋I am looking to understand if anyone else has found a way to group documents by their published date with the desk tool plugin? I’ve seen a number of examples in the docs about filtering by categories etc, but less so about grouping by date.

The problem I have here is that users may enter 8-10 documents per day so this list is going to get very big. It would be great if they could see “4th May 2020”, “5th May 2020" etc under the “My Readings” panel, then click to see all the documents from that day.

Any advise would be appreciated
🙏
AI Update

Grouping documents by published date in the Structure Tool is definitely achievable, though it requires a bit of creative use of the Structure Builder API. The main challenge is that the Structure Builder doesn't have a built-in "group by date" feature, so you'll need to manually create list items for your date groupings.

Here are the main approaches you can take:

Approach 1: Static Date-Based Lists

The most straightforward (and performant) approach is to create predefined date ranges that filter your documents. This works well for common use cases:

import type {StructureResolver} from 'sanity/structure'

export const structure: StructureResolver = (S) =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('My Readings')
        .child(
          S.list()
            .title('Readings by Date')
            .items([
              S.listItem()
                .title('Today')
                .child(
                  S.documentList()
                    .title("Today's Readings")
                    .filter('_type == "reading" && publishedAt >= $today')
                    .params({
                      today: new Date().toISOString().split('T')[0]
                    })
                ),
              S.listItem()
                .title('Yesterday')
                .child(
                  S.documentList()
                    .title("Yesterday's Readings")
                    .filter('_type == "reading" && publishedAt >= $yesterday && publishedAt < $today')
                    .params({
                      yesterday: new Date(Date.now() - 86400000).toISOString().split('T')[0],
                      today: new Date().toISOString().split('T')[0]
                    })
                ),
              S.listItem()
                .title('This Week')
                .child(
                  S.documentList()
                    .title("This Week's Readings")
                    .filter('_type == "reading" && publishedAt >= $weekStart')
                    .params({
                      weekStart: new Date(Date.now() - 7 * 86400000).toISOString().split('T')[0]
                    })
                ),
              // Add more date ranges as needed
            ])
        ),
    ])

This approach is reliable, performs well, and doesn't require any async operations. The downside is that it's not truly dynamic—you're defining fixed date ranges rather than automatically creating a list item for each unique date.

Approach 2: Dynamic Date Grouping (With Caveats)

While the Structure Builder does support async functions, using them to fetch unique dates and dynamically create list items comes with important performance considerations. The Structure Builder will re-evaluate these async functions frequently, potentially causing performance issues as your document count grows.

If you still want to pursue this approach, be aware that you'll be making client queries every time the structure loads. Here's the pattern, but use it cautiously:

import type {StructureResolver} from 'sanity/structure'

export const structure: StructureResolver = (S, context) =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('My Readings')
        .child(async () => {
          const client = context.getClient({apiVersion: '2024-01-01'})
          
          // This query will run frequently - consider performance implications
          const documents = await client.fetch(`
            *[_type == "reading" && defined(publishedAt)] 
            | order(publishedAt desc) [0...50] {
              publishedAt
            }
          `)
          
          // Group by date in JavaScript
          const dateGroups = documents.reduce((groups: Record<string, number>, doc: any) => {
            const date = doc.publishedAt.split('T')[0]
            groups[date] = (groups[date] || 0) + 1
            return groups
          }, {})
          
          return S.list()
            .title('My Readings by Date')
            .items(
              Object.entries(dateGroups).map(([date, count]) =>
                S.listItem()
                  .title(`${new Date(date).toLocaleDateString('en-GB', {
                    day: 'numeric',
                    month: 'long',
                    year: 'numeric'
                  })} (${count})`)
                  .id(date)
                  .child(
                    S.documentList()
                      .title(`Readings from ${date}`)
                      .filter('_type == $type && publishedAt >= $start && publishedAt < $end')
                      .params({
                        type: 'reading',
                        start: `${date}T00:00:00Z`,
                        end: `${date}T23:59:59Z`
                      })
                  )
              )
            )
        }),
    ])

Important limitations of this approach:

  • The query runs every time the structure is evaluated, which can impact performance
  • I've limited the query to 50 documents to avoid excessive processing—adjust based on your needs
  • With 8-10 documents per day, this could become slow as your content grows
  • Consider caching strategies if you go this route

Recommendation

For your use case with 8-10 documents per day, I'd recommend starting with Approach 1 (static date ranges) using categories like "Today", "Yesterday", "This Week", "This Month", etc. This provides excellent performance and covers most editorial workflows.

If you absolutely need to see every unique date, consider implementing a custom component using the Structure Builder's component integration that can handle the date grouping logic with better control over caching and performance.

The Structure Builder documentation has more examples of filtered lists, and the pattern of using S.documentList() with .filter() and .params() is the foundation for any date-based filtering approach.

Hi! Yes you can do this with the structure builder
In other words, something like this
user H
https://gist.github.com/kmelve/c3a1d42ee210649aefac3e5e7e4716e0
That's perfect, thank you
user A
and
user Y
. I had seen in the docs that grouping by published date was a valid use case, but wasn't entirely sure of the best approach to take with the GROQ queries and the structure builder API. This is awesome Knut, thanks so much.
You can pretty much do “anything” with Structure builder.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?