Storyblok
Introduction
The SCAYLE Storefront Boilerplate includes a default integration that allows you to use Storyblok as a CMS. It includes various helpers to quickly introduce custom content into a Storefront Boilerplate-based shop application.
The Storefront Boilerplate uses @storyblok/nuxt
in the background. For further details consult the Storyblok documentation.
Quick links
- Storyblok Developer Guides
- Storyblok Editor Guides
- Storyblok Youtube Walkthrough
Storefront Integration
Local Preview
You can preview your local environment within Storyblok:
Storyblok Local Environment
Product data
To link to your shop data, the Storefront Boilerplate adds fields in Storyblok that you can then fill with your desired categories and products:
Ready Components
The following components are implemented as part of the Storefront Boilerplate, which means they are available out-of-the-box for all shops that are scaffolded from the Storefront Boilerplate - that is, all SCAYLE shops.
Base Storyblok Components
Accordion
AccordionEntry
Banner
BannerLink
ClickableImage
CmsImage
CmsLink
CmsText
CmsVideo
DetailImage
Grid
GridTile
ImageSlider
ImageSliderSlide
ImageText
Page
Product
ProductSlider
ScrollableLinkList
SlideShow
Story
StoryblokLink
Technical Details
The Storyblok integration with Storefront Boilerplate is separated between local development (using storyblok cli
) and shop runtime (using @storyblok/nuxt
).
Initial development setup
To use Storyblok as your CMS, add the following configuration to your nuxt.config.ts:
export default defineNuxtConfig({
cms: {
provider: 'storyblok'
},
})
The initial setup of Storyblok within Storefront Boilerplate requires the creation of a dedicated local .env.storyblok
file. The local .env.storyblok
will contain all Storyblok-relevant environment variables necessary to work locally with the Storyblok.
STORYBLOK_PERSONAL_TOKEN=<INSERT-YOUR-TOKEN-HERE>
STORYBLOK_SPACE_ID=<INSERT-YOUR-SPACE-ID-HERE>
export default defineNuxtConfig({
runtimeConfig: {
cms: {
accessToken: '', // Overide: NUXT_PUBLIC_CMS_ACCESS_TOKEN
},
},
})
Create a STORYBLOK_PERSONAL_TOKEN
A STORYBLOK_PERSONAL_TOKEN
can be created as part of the Storyblok Account Settings.
Source the STORYBLOK_SPACE_ID
The STORYBLOK_SPACE_ID
can be found as part of the URL when logged into storyblok.com and accessing the appropriate Storyblok space.
Scripts
As part of the Storefront Boilerplate package.json
, some additional scripts are included to interact with Storyblok. These are:
storyblok:download
- Downloads the latest components from the respective Storyblok space using Storyblok CLI
storyblok:generate
- Uses the downloaded components JSON schema and transforms it into TypeScript types (See section "Type Definitions")
storyblok:login
- Authenticates local development environment with Storyblok CLI
storyblok:unused
- Outputs overview of used and unused Storyblok components
While storyblok:download
and storyblok:login
are directly utilizing the Storyblok CLI, storyblok:generate
and storyblok:unused
are executing dedicated .cjs
scripts.
storyblok:generate
The storyblok:generate
script, located at scripts/storyblok-generate.cjs
, creates a TypeScript type definition based on the local Storyblok components JSON schema. The JSON schema needs to be downloaded before running this command.
The generated TypeScript type definition will be located at storyblok/types/storyblok.gen.d.ts
.
To create the generated type definition, the script uses a custom internal NPM package called storyblok-generate-ts
, which provides a configurable transformation function storyblokToTypescript(.
This function facilitates the actual transformation of the JSON schema and outputs the type definition based on the passed configuration object, which is pre-configured for usage with Storefront Boilerplate.
storyblok:unused
The storyblok:unused
script, located at scripts/storyblok-unused.cjs
, creates a list of all unused components of a space by utilizing the Storyblok Management API.
You can find the original script here.
Type Definitions
storyblok/types/storyblok.d.ts
- Manual type definition
storyblok/types/storyblok.gen.d.ts
- Generated type definition
Local Dev
The process to synchronize a Storefront Boilerplate project during local development is identical for both new and existing projects.
Basic Composables and Helpers
To simplify the usage of certain functionalities with Storyblok components, the SCAYLE Storefront Boilerplate includes a few simple reusable composables:
storyblok/composables/useCmsAlignment.ts
- Provides Tailwind-compatible alignment classes to be used with Storyblok components
storyblok/composables/useStoryblokImage.ts
- Provides utilities process and return a sanitized teaser image object to be used with Storyblok components
storyblok/composables/useStoryblokMargins.ts
- Provides Tailwind-compatible margin classes to be used with Storyblok components
composables/useCmsListingContent.ts
- Returns a formatted object for usage with listings containing the CMS
content
,preListingContent
,postListingContent
andhasTeaserImage
- Returns a formatted object for usage with listings containing the CMS
Shop Runtime
The runtime integration between Storyblok and the SCAYLE Storefront Boilerplate is achieved by using the @storyblok/nuxt
module.
The configuration can be extended under the storyblok
key as part of the nuxt.config.ts
. Refer to the official Storyblok module documentation for more information and configuration options.
The default configuration only sets the STORYBLOK_ACCESS_TOKEN
and enables the bridge
option. The bridge
option is used for integrating with the Nuxt framework and with the Storyblok visual editor.
The integration additionally relies on the @nuxt/image
module (See NuxtImage module documentation) to handle images used with Storyblok components.
Deployment Requirements
The @storyblok/nuxt
module provides an integration with the Nuxt RuntimeConfig functionality. This means that the NUXT_STORYBLOK_ACCESS_TOKEN
can be set during the application's runtime as an environment variable instead of during build time.
Auto-imported components
Storyblok components are auto-imported from the storyblok/
directory at the root of the project and the components are made available throughout the project. Be mindful of component name collisions. If your component in the ~/components
directory is named the same as the one inside ~/storyblok
there can be issues with the Storyblok auto imported components.
The StoryblokComponent
is also auto imported and can be used out-of-the-box.
Fetching content
Storefront Boilerplate provides a simple way to fetch content from Storyblok.
useCMS
As content from Storyblok allows references to data provided by SCAYLE, for example product data, the SCAYLE Storefront Boilerplate features the useCMS
composable. This composable internally relies on the storyblok-js-client
package to fetch.
useCMS accepts the key argument that will be used as a key for useAsyncData for storing values.
An example usage for getting relevant content might look like the following:
const {
data, status
} = await useCMSBySlug<SbFooter>('footer', 'global/footer')
useCMS
is a file that exposes the following two dedicated composables:
useCMSBySlug
- Wraps async function around
useAsyncData
anduseStoryblokApi
composable to fetch Storyblok content by slug using the slug formatcdn/stories/${slug}
.
- Wraps async function around
useCMSByFolder
- Wraps async function around
useAsyncData
anduseStoryblokApi
composable to fetch Storyblok content by folder using thestarts_with
parameter.
As the composablesuseCMSBySlug
anduseCMSByFolder
wrap arounduseAsyncData,
you then have the whole type signature. For more details on all returned data, refer to the Nuxt documentation.- Wraps async function around
import type { SbFooter } from '~/modules/cms/providers/storyblok/types'
import { useCMSBySlug } from '~/modules/cms/providers/storyblok/composables/useCMS'
const props = defineProps<{
slug: string
}>()
const { fetchBySlug } = useCMS(`content-page-${props.slug}`)
const { data, error, status } = await useCMSBySlug<SbContentPage>(
`content-page-${props.slug}`,
`content/${props.slug}`,
)
While the Nuxt 2-based implementation allowed for Storyblok content to be manually cache-controlled, this feature set is currently not available for the Nuxt 3-based implementation.
Live Preview
To display unpublished changes on the initial load within the Storyblok editor, the access token needs the Preview
access level. If the access token has a different access level, unpublished changes will only appear after modifications are made within the editor.
Components
This section offers a detailed overview of various shop areas utilizing Storyblok content, alongside their configuration options. It's essential to review the type definitions to understand the optional values and the expected data types for each configuration option.
Homepage
Component: SlideShow
Parameter | Details |
---|---|
h1 | String |
slides | SbSlide[] |
margin_top |
Accepts |
Component: Slide
Parameter | Details |
---|---|
image | SbCmsImage | SbVideo |
topline | String |
headline | String |
cta_label | String |
cta_url | String |
is_dark | Boolean |
align |
Accepts |
justify |
Accepts |
Component: ImageSlider
Parameter | Details |
---|---|
image | SbCmsImage | SbVideo |
headline | String |
cta_label | String |
cta_url | String |
slides | SbImageSliderSlide[] |
margin_top |
Accepts |
Component: ImageSliderSlide
Parameter | Details |
---|---|
image | SbCmsImage | SbVideo |
topline | String |
headline | String |
is_new | Boolean |
cta_label | String |
cta_url | String |
item_id | String |
item_name | String |
promotion_id | String |
promotion_name | String |
creative_name | String |
creative_slot | String |
Component: Grid
Parameter | Details |
---|---|
margin_top |
Accepts |
is_containered | Boolean |
is_containered_desktop | Boolean |
is_spaced | Boolean |
columns | SbShopableImage |
Component: GridTile
- Options:
headline
: accepts a stringcta
: accepts a stringcta_link
: accepts a link
Parameter | Details |
---|---|
content | SbShopableImage |
headline | String |
cta | String |
cta_link | Sbmultilink |
Component: ImageText
Parameter | Details |
---|---|
image |
Accepts |
image | SbCmsImage[] |
topline | String |
headline | String |
text | String |
cta | String |
cta_link | Sbmultilink |
justify |
Accepts |
Component: ProductSlider
Parameter | Details |
---|---|
headline | String |
cta_label | String |
cta_url | String |
product_ids |
References to SCAYLE products |
margin_top |
Accepts |
Component: Banner
Parameter | Details |
---|---|
is_active | Boolean |
type |
Accepts |
body | String |
countdown_until | String |
links | SbBannerLink[] |
item_id | String |
item_name | String |
promotion_id | String |
promotion_name | String |
creative_name | String |
creative_slot | String |
location_id | String |
index | String |
cta_url | Sbmultilink |
Component: Story
Parameter | Details |
---|---|
color | String |
image | Sbasset |
label | String |
cta_url | String |
Footer
Component: Footer
Parameter | Details |
---|---|
text | Richtext |
align_right | Boolean |
link_groups | SbLinkGroup[] |
text_bottom | String |
social_media | SbSocialMediaLink[] |
Component: LinkGroup
Parameter | Details |
---|---|
title | String |
links | SbLink[] |
Component: Link
Parameter | Details |
---|---|
label | String |
cta_url | Sbmultilink |
open_in_new_tab | Boolean |
Listing Pages
Component: ListingPage
Parameter | Details |
---|---|
teaser_image | Sbasset |
teaser_image_mobile | Sbasset |
pre_listing_content | SbAccordion | SbAccordionEntry | SbBanner | SbBannerLink | SbClickableImage | SbCmsImage | SbCmsText | SbContentPage | SbDetailImage | SbDoubleColumn | SbFooter | SbGrid | SbGridTile | SbImageSlider | SbImageSliderSlide | SbImageText | SbLink | SbLinkGroup | SbListingPage | SbPage | SbParagraph | SbProduct | SbProductSlider | SbShopableImage | SbSlide | SbSlideShow | SbSocialMediaLink | SbStory | SbTitle | SbVideo |
SEO |
|
title: string | |
plugin?: string | |
og_image?: string | |
og_title?: string | |
description?: string | |
twitter_image?: string | |
twitter_title?: string | |
og_description?: string | |
twitter_description?: string | |
} | |
post_listing_content | SbAccordion | SbAccordionEntry | SbBanner | SbBannerLink | SbClickableImage | SbCmsImage | SbCmsText | SbContentPage | SbCustomerServiceInfo | SbDetailImage | SbDoubleColumn | SbFooter | SbGrid | SbGridTile | SbImageSlider | SbImageSliderSlide | SbImageText | SbLink | SbLinkGroup | SbListingPage | SbPage | SbParagraph | SbProduct | SbProductSlider | SbShopableImage | SbSlide | SbSlideShow | SbSocialMediaLink | SbStory | SbTitle | SbVideo |
Service Pages
Component: Accordion
- Options:
has_link_list
: accepts a booleanentries
: accept a list ofSbAccordionEntry
margin_top
: accepts a string
Parameter | Details |
---|---|
has_link_list | Boolean |
entries | SbAccordionEntry [] |
margin_top |
Accepts |
Component: AccordionEntry
Parameter | Details |
---|---|
title | String |
link_title | String |
body | Richtext |
Area: Content Pages
Component: Content Page
Parameter | Details |
---|---|
teaser_image | Sbasset |
teaser_image_mobile | Sbasset |
headline | String |
subline | String |
content | SbCmsImage | SbCmsText | SbGrid | SbImageSlider | SbProductSlider | SbSlideShow | SbSocialMediaLink | SbVideo | SbAccordion | SbCustomerServiceInfo | SbDoubleColumn | SbParagraph |
SEO |
|
title: string | |
plugin?: string | |
og_image?: string | |
og_title?: string | |
description?: string | |
twitter_image?: string | |
twitter_title?: string | |
og_description?: string | |
twitter_description?: string | |
} | |
All Pages
Component: Video
Parameter | Details |
---|---|
control_color | String |
-autoplay | Boolean |
has_controls | Boolean |
is_containered | Boolean |
video | Sbasset |
preview_desktop_image | Sbasset |
preview_mobile_image | Sbasset |
loop | Boolean |
item_id | String |
item_name | String |
promotion_id | String |
promotion_name | String |
creative_name | String |
creative_slot | String |
location_id | String |
margin_top |
Accepts |
Component: ClickableImage
Parameter | Details |
---|---|
image | SbCmsImage[] |
cta_url | Sbmultilink |
item_id | String |
item_name | String |
promotion_id | String |
promotion_name | String |
creative_name | String |
creative_slot | String |
location_id | String |
margin_top |
Accepts |
Component: CmsImage
Parameter | Details |
---|---|
desktop_image | Sbasset |
mobile_image | Sbasset |
item_id | String |
item_name | String |
promotion_id | String |
promotion_name | String |
creative_name | String |
creative_slot | String |
location_id | String |
index | String |
Component: CmsText
Parameter | Details |
---|---|
body | Richtext |
Component: DoubleColumn
Parameter | Details |
---|---|
column_right | SbAccordion | SbAccordionEntry | SbBanner | SbBannerLink | SbClickableImage | SbCmsImage | SbCmsText | SbContentPage | SbDetailImage | SbDoubleColumn | SbFooter | SbGrid | SbGridTile | SbImageSlider | SbImageSliderSlide | SbImageText | SbLink | SbLinkGroup | SbListingPage | SbPage | SbParagraph | SbProduct | SbProductSlider | SbShopableImage | SbSlide | SbSlideShow | SbSocialMediaLink | SbStory | SbTitle | SbVideo |
column_left | SbAccordion | SbAccordionEntry | SbBanner | SbBannerLink | SbClickableImage | SbCmsImage | SbCmsText | SbContentPage | SbCustomerServiceInfo | SbDetailImage | SbDoubleColumn | SbFooter | SbGrid | SbGridTile | SbImageSlider | SbImageSliderSlide | SbImageText | SbLink | SbLinkGroup | SbListingPage | SbPage | SbParagraph | SbProduct | SbProductSlider | SbShopableImage | SbSlide | SbSlideShow | SbSocialMediaLink | SbStory | SbTitle | SbVideo |
margin_top |
Accepts |
Component: Paragraph
Parameter | Details |
---|---|
headline | String |
cta | Sbmultilink |
body | Richtext |
sub_title | String |
image | Sbmultiasset |