Queries

Overview

A query is a request to the Storyblok API to retrieve data. For this scenario we have made <SbQuery> which act as a wrapper for multiple requests.

A single story can be fetched manually with the Storyblok API

Use-Cases

<SbQuery> is a powerful component for showing a list of data from Storyblok API.

  • Use it as wrapper for your custom lists and grid designs in your website (Team members, products, portfolio items)
  • Archive pages for news or blog items
  • Filter and sort the data
  • Make items searchable inside SbQuery

Simple Queries

a) Slot approach

  • Retrieve multiple posts from the posts folder in storyblok.
  • Use default slot with scoped story object.
components/bloks/BlokPostList.vue
<SbQuery path="posts">
    <template #default="story">
        <li>
            <nuxt-link :to="story.full_slug">
                <h3>{{ story.content.title }}</h3>
                <p>{{ story.content.excerpt }}...</p>
            </nuxt-link>
        <li>
    </template>
</SbQuery>

Create reusable components:

components/bloks/BlokPostList.vue
<SbQuery path="posts">
    <template #default="story">
        <SingleArticle v-bind="story" />
    </template>
</SbQuery>
components/atoms/SingleArticle.vue
<template>
    <li>
        <nuxt-link :to="story.full_slug">
            <h3>{{ story.content.title }}</h3>
            <p>{{ story.content.excerpt }}...</p>
        </nuxt-link>
    <li>
</template>
<script>
export default {
    props: {
        story: {
            type: Object,
            default: () => ({})
        }
    }
}
</script>

b) Content type approach

With <SbQuery> we are retriving a list of stories based on a content type. You can create a separete vue component with the name of this content type (e.g. Job.vue). <SbQuery> will look for the component with same name as the content type and includes it.

Retrieve multiple posts with content-type Post.

components/bloks/BlokPostList.vue
<SbQuery path="posts" />
components/content-types/Post.vue
<template>
  <li>
    <nuxt-link :to="story.full_slug">
      {{ story.content.title }}
    </nuxt-link>
  </li>
</template>

<script>
export default {
  props: {
    blok: {
      type: Object,
      default: () => ({})
    }
  }
}
</script>
Which approach should I use? We prefer the slot approach which represents clear parent -> child hierachy than the Content type approach. It always depends on use case.

Pagination

For longer lists you might want to split the retrieved content into multiple pages. As of now we can choose between infinite, numeric and none pagination types.

Example for infinite pagination with a Load more (i18n ready) button.

<SbQuery path="portfolio" pagination-type="infinite" />

Example for a numeric pagination example

<SbQuery path="portfolio" pagination-type="numeric" />

Complete custom pagination template.

<SbQuery path="portfolio">
    <template #pagination="{loadMore, currentPage, total}">
        {{ currentPage }} / {{ total }}
        <button @click="loadMore">
            LOAD MORE
        </button>
    </template>
</SbQuery>

Full example

<SbQuery :options="{per_page: 1}" path="jobs" :filter-client-only="false">
    <template #default="story">
        <JobItem :title="story.content.title" :salary="story.content.salary" :link="story.full_slug" />
    </template>
    <template #pagination="{loadMore, currentPage, total}">
        {{ currentPage }} / {{ total }}
        <button @click="loadMore">
            LOAD MORE
        </button>
    </template>
</SbQuery>

Filter and sort strategy

This affects

  • Pagination
  • Filters
  • Sorting

We offer two strategies how you can handle pagination logic. This is controlled with the filterClientOnly prop.

Client only

  • filterClientOnly: true: Auto fetch 100 (max. from Storyblok) stories and do all logic on clientside (without refetching from server). This is recommend for small data where server requests not make much sense.

Advantages:

  • Very fast. No additional server requests needed.

Disadvantages:

  • Limited to max. 100 posts at once.
<SbQuery path="jobs" :posts-per-page-client-only="25" :filter-client-only="true" />

Server fetching

  • filterClientOnly: false: Recommend for bigger amount of stories. With this approach you fetch piece by piece from server for each page. Filter and sort is also handled by server requests via Storybok API.
<SbQuery path="jobs" :filter-client-only="false" />

Advantages:

  • Always consistent since we're fetching from server.
  • Support unlimited data > 100 entries

Disadvantages:

  • Limited to max. 100 posts at once.

  • For every filter, sort process a request to the server will be made.

  • Not recommend for small lists.

Render hooks

afterFetchCollection

Sometimes you need to manipulate data from outside and use this updated data in the rendered template. You can hook into afterFetchCollection hook and mutate the given Storyblok collection.

For example if you want to remove expired jobs on client before render:

<template>
    <ul>
        <SbQuery path="jobs" @afterFetchCollection="removeExpiredJobs">
            <template #default="item">
                <JobItem :title="item.content.title" :salary="item.content.salary" :link="item.full_slug" />
            </template>
        </SbQuery>
    </ul>
</template>
<script>
export default {
  props: {
    bloks: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    removeExpiredJobs(collection) {
        return this.collection.filter((job) => {
            if (job.content.expired === false) {
                return job
            }
        })
    }
  }
}
</script>

Component Api

<SbQuery>

Props

Prop Default Type Description
`options` { sort_by: 'created_at:desc' } `Object`
Any options from Storyblok API Documentation
`path` `` `String`
Filter by full_slug to retrieve data from a specific folder. Uses `starts_with` option.
`paginationType` `infinite` `string`
Choose a pagination type. infinite, numeric, none
`filterQuery` `{}` `Object`
Add a custom caption text
`disableFetch` `false` `Boolean`
You can disable remote fetching. Use this in combination with dataSource
`dataSource` `false` `Boolean`
SbQuery is basically designed to fetch remote data. But you can fill it with a custom data array and use it other features like pagination e.g.
`postsPerPageClientOnly` `12` `Number`
How many items should be shown on one page. Use `-1` to show unlimited items. This is a client only solution. If you want to limit posts on server fetching use `options.per_page` prop.
`filterClientOnly` `true` `Boolean`
See [Filter and sort strategy](/components/queries#filter-and-sort-strategy)