Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/storyblok/gatsby-source-storyblok
Gatsby source plugin for building websites using the Storyblok headless CMS as a data source.
https://github.com/storyblok/gatsby-source-storyblok
dx gatsby product storyblok
Last synced: 7 days ago
JSON representation
Gatsby source plugin for building websites using the Storyblok headless CMS as a data source.
- Host: GitHub
- URL: https://github.com/storyblok/gatsby-source-storyblok
- Owner: storyblok
- License: mit
- Created: 2018-06-26T08:01:46.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-10-11T08:55:22.000Z (3 months ago)
- Last Synced: 2024-10-30T05:07:02.494Z (3 months ago)
- Topics: dx, gatsby, product, storyblok
- Language: JavaScript
- Homepage:
- Size: 4.84 MB
- Stars: 43
- Watchers: 17
- Forks: 35
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
gatsby-source-storyblok
The Gatsby source plugin you need to interact with Storyblok API and enable the Real-time Visual Editing Experience.
## đ Usage
> If you are first-time user of the Storyblok, read the [Getting Started](https://www.storyblok.com/docs/guide/getting-started?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok) guide to get a project ready in less than 5 minutes.
### Installation
Install `gatsby-source-storyblok`:
```bash
npm install gatsby-source-storyblok
// yarn add gatsby-source-storyblok
```#### Compatibility
| Version to install | Support |
| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Latest >v6 `gatsby-source-storyblok` | Modern browsers + Node 16+. [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) is implemented. Supports Gatsby 5 (React Server Components) |
| [Version 5](https://github.com/storyblok/gatsby-source-storyblok/tree/v5) `gatsby-source-storyblok@5 ` | Modern browsers + Node 16+. [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) is implemented. (Go to [v5 branch](https://github.com/storyblok/gatsby-source-storyblok/tree/v5) for Gatsby 4 and lower Gatsby version support) |
| [Version 4](https://github.com/storyblok/gatsby-source-storyblok/tree/v4.2.1) `gatsby-source-storyblok@4` | Node 14 and lower Node with no Fetch API support |### Initialization
Register the plugin on your application and add the [access token](https://www.storyblok.com/docs/api/content-delivery#topics/authentication?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok) of your Storyblok space. You can also add the `apiPlugin` in case that you want to use the Storyblok API Client.
> You need to declare the plugin use and its options in `gatsby-config.js`
`gatsby-config.ts`
```JavaScript
import type { GatsbyConfig } from "gatsby"const config: GatsbyConfig = {
flags: {
PARTIAL_HYDRATION: true // Required for Partial Hydration (RSC)
},
graphqlTypegen: true,
plugins: [
{
resolve: 'gatsby-source-storyblok',
options: {
accessToken: '',
version: 'draft',
localAssets: true, // Optional parameter to download the images to use with Gatsby Image Plugin
languages: ['de', 'at'] // Optional parameter. Omission will retrieve all languages by default.
}
},
],
}
````src/components/layout.jsx`
```JavaScript
"use client" // Required for Partial Hydration, client components (RSC)
import configuration from '../../gatsby-config'const sbConfig = configuration.plugins.find((item) => item.resolve === 'gatsby-source-storyblok')
storyblokInit({
accessToken: sbConfig.options.accessToken,
// bridge: false,
apiOptions: {
region: "us", // Pass this key/value if your space was created under US region
},
use: [apiPlugin],
components: {
teaser: Teaser,
grid: Grid,
feature: Feature,
page: Page
}
});
```> Add all your components to the components object in the `storyblokInit` function.
That's it! All the features are enabled for you: the _API Client_ for interacting with [Storyblok CDN API](https://www.storyblok.com/docs/api/content-delivery#topics/introduction?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok), and _Storyblok Bridge_ for [real-time visual editing experience](https://www.storyblok.com/docs/guide/essentials/visual-editor?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok).
> You can enable/disable some of these features if you don't need them, so you save some KB. Please read the "Features and API" section
#### Region parameter
Possible values:
- `eu` (default): For spaces created in EU
- `us` : For spaces created in the US
- `ap`: For spaces created in Australia
- `ca`: For spaces created in Canada
- `cn` : For spaces created in ChinaFull example for a space created in the US:
```javascript
storyblokInit({
accessToken: "",
apiOptions: {
region: "us",
},
});
```### Getting Started
`gatsby-source-storyblok` does three actions when you initialize it:
- Provides a `` component to enable live-editting experience for a story, automatically.
- Loads [Storyblok Bridge](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok) for real-time visual updates.
- Provides a `storyblokEditable` function to link editable components to the Storyblok Visual Editor.> [gatsby-source-storyblok v5](https://github.com/storyblok/gatsby-source-storyblok/tree/v5) and lower had `useStoryblokState` that parsed story content JSON into the object. Use `` instead to handle the Visual Editor live events when editing story.
#### 1. Fetching Content & Listen to Storyblok Visual Editor events
Query data from GraphQL:
`src/pages/index.tsx`
```js
import { StoryblokStory } from "gatsby-source-storyblok";import Layout from "../components/layout";
const IndexPage = ({ data }) => {
if (typeof data.storyblokEntry.content === "string")
data.storyblokEntry.content = JSON.parse(data.storyblokEntry.content);return (
);
};export default IndexPage;
export const query = graphql`
query HomeQuery {
storyblokEntry(full_slug: { eq: "home" }) {
name
full_slug
}
}
`;
```> Note: if you don't use `apiPlugin`, you can use your prefered method or function to fetch your data.
Use `` to get the new story every time is triggered a `change` event from the Visual Editor.
#### 2. Link your components to Storyblok Visual Editor
`` keeps the state for the story behind the scenes and uses `StoryblokComponent` to render the route components dynamically, using the list of components loaded during the initialization inside the storyblokInit function. You can use the `StoryblokComponent` inside the components to redner the nested components dynamically. You can also pass bridge options to `` using the prop `bridgeOptions`.
```js
```
For every component you've defined in your Storyblok space, call the `storyblokEditable` function with the blok content:
`src/components/Page.tsx`
```js
import { storyblokEditable, StoryblokComponent } from "gatsby-source-storyblok";const Page = ({ blok }) => (
{blok.body.map((nestedBlok) => (
))}
);export default Page;
````src/components/Feature.jsx`
```js
import { storyblokEditable } from "gatsby-source-storyblok";const Feature = ({ blok }) => {
return (
{blok.name}
{blok.description}
);
};export default Feature;
```Where `blok` is the actual blok data coming from [Storblok's Content Delivery API](https://www.storyblok.com/docs/api/content-delivery?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok).
As an example, you can check in our [Gatsby.js example demo](https://github.com/storyblok/gatsby-source-storyblok/tree/master/playground) how we use APIs provided from React SDK to combine with Gatsby.js projects.
### Features and API
You can **choose the features to use** when you initialize the plugin. In that way, you can improve Web Performance by optimizing your page load and save some bytes.
#### Storyblok API
You can use an `apiOptions` object. This is passed down to the [storyblok-js-client config object](https://github.com/storyblok/storyblok-js-client#class-storyblok):
```js
storyblokInit({
accessToken: "YOUR_ACCESS_TOKEN",
apiOptions: {
// storyblok-js-client config object
cache: { type: "memory" },
},
use: [apiPlugin],
components: {
teaser: Teaser,
grid: Grid,
feature: Feature,
},
});
```If you prefer to use your own fetch method, just remove the `apiPlugin` and `storyblok-js-client` won't be added to your application.
```js
storyblokInit({});
```#### Storyblok Bridge
If you don't use `registerStoryblokBridge`, you still have access to the raw `window.StoryblokBridge`:
```js
const sbBridge = new window.StoryblokBridge(options);sbBridge.on(["input", "published", "change"], (event) => {
// ...
});
```#### Rendering Rich Text
You can easily render rich text by using the `renderRichText` function that comes with `gatsby-source-storyblok`:
```js
import { renderRichText } from "gatsby-source-storyblok";
const renderedRichText = renderRichText(blok.richtext);
```You can set a **custom Schema and component resolver globally** at init time by using the `richText` init option:
```js
import { RichTextSchema, storyblokInit } from "gatsby-source-storyblok";
import cloneDeep from "clone-deep";
const mySchema = cloneDeep(RichTextSchema); // you can make a copy of the default RichTextSchema
// ... and edit the nodes and marks, or add your own.
// Check the base RichTextSchema source here https://github.com/storyblok/storyblok-js-client/blob/master/source/schema.js
storyblokInit({
accessToken: "",
richText: {
schema: mySchema,
resolver: (component, blok) => {
switch (component) {
case "my-custom-component":
return `${blok.text}`;
default:
return "Resolver not defined";
}
},
},
});
```You can also set a **custom Schema and component resolver only once** by passing the options as the second parameter to `renderRichText` function:
```js
import { renderRichText } from "gatsby-source-storyblok";
renderRichText(blok.richTextField, {
schema: mySchema,
resolver: (component, blok) => {
switch (component) {
case "my-custom-component":
return `${blok.text}`;
break;
default:
return `Component ${component} not found`;
}
},
});
```### Gatsby feature references
### Partial Hydration (RSC)
To enable Partial Hydration, you need to set the `PARTIAL_HYDRATION` flag to `true` in `gatsby-config` file. Here is an example of the usage:
```js
module.exports = {
flags: {
PARTIAL_HYDRATION: true,
},
};
```When enabling Partial Hydration, all components act as server component by default. For more details such as benefits in terms of performance, please refer to the [Gatsby docs](https://www.gatsbyjs.com/docs/how-to/performance/partial-hydration/).
To define a component to act as a client component, use `"use client"` directive in the first line of the code. Here is an example of the usage:
`layout.jsx`
```js
"use client"; // Required for Partial Hydration, client components (RSC)
import configuration from "../../gatsby-config";const sbConfig = configuration.plugins.find(
(item) => item.resolve === "gatsby-source-storyblok"
);storyblokInit({
// ...
});
```> Partial Hydration is a Beta feature. For limitations, see Gatsby documentation: [Partial Hydration, Limitations](https://www.gatsbyjs.com/docs/how-to/performance/partial-hydration/#limitations)
#### With Gatsby's image
You need to set the `localAssets` option to `true`. Here is an example of the usage:
```js
import { graphql } from "gatsby";
import { GatsbyImage, getImage } from "gatsby-plugin-image";function BlogPost({ data }) {
const image = getImage(data.file);
return (
);
}export const pageQuery = graphql`
query {
file(name: { eq: "demo" }) {
absolutePath
url
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`;
```#### With Gatsby's createPages
For more info regarding `createPages` see the Gatsby docs: [docs/reference/config-files/gatsby-node/#createPages](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/#createPages)
2a. You need to create a [template](https://www.gatsbyjs.org/docs/programmatically-create-pages-from-data/#specifying-a-template) file to get the data from GraphQL
```js
import { StoryblokStory } from "gatsby-source-storyblok";
import Layout from "../components/layout";export default function StoryblokEntry({ data }) {
return (
);
}export const query = graphql`
query ($slug: String!) {
storyblokEntry(full_slug: { eq: $full_slug }) {
internalId
name
full_slug
}
}
`;
```3a. After this, you need to create the pages for your application. For this, edit your `gatsby-node.js`.
```js
const path = require("path");exports.createPages = async ({ graphql, actions }) => {
const storyblokEntry = path.resolve("src/templates/storyblok-entry.js");// querying the storyblok data from GraphQL data layer
const { data } = await graphql(`
query {
allStoryblokEntry {
edges {
node {
internalId
full_slug
}
}
}
}
`);// creating pages using createPage function like described in the documentation
// https://www.gatsbyjs.org/docs/programmatically-create-pages-from-data/#creating-pages
data.allStoryblokEntry.edges.forEach((edge) => {
const full_slug = edge.node.full_slug;actions.createPage({
path: full_slug,
component: storyblokEntry,
context: {
slug: full_slug,
},
});
});
};
```#### With Gatsby's File System Routes API
For more info regarding The File System Routes API see the Gatsby docs: [docs/reference/routing/file-system-route-api/](https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api/)
2b. Create a collection route inside `src/pages`
```
|-- src
|-- pages
|-- {storyblokEntry.full_slug}.js
```3b. Gatsby will use ths page template for each `storyblokEntry`
```js
import { StoryblokStory } from "gatsby-source-storyblok";import Layout from "../components/layout";
export default function StoryblokEntry({ data }) {
return (
);
}export const query = graphql`
query ($full_slug: String!) {
storyblokEntry(full_slug: { eq: $full_slug }) {
internalId
name
full_slug
}
}
`;
```#### The options object in details
`gatsby-config.js`
```js
{
resolve: 'gatsby-source-storyblok',
options: {
accessToken: 'YOUR_TOKEN',
version: 'draft',
resolveRelations: [''],
includeLinks: false
}
}
```- `accessToken`: Your Storyblok draft token
- `version`: 'draft' or 'published'
- `timeout`: Optionally provide a timeout for the api request
- `resolveLinks`: This will automatically resolve internal links of the multilink field type. If the value is `story` the whole story object will be included. If the value is `url` only uuid, id, name, path, slug and url (url is a computed property which returns the "Real path" if defined to use it for navigation links) will be included.
- `resolveRelations`: Resolve relationships to other Stories (in the first level of nesting) of a multi-option or single-option field-type. Provide the field key(s) as array to resolve specific fields. Example: ['article.related_articles', 'article.author'].
- `includeLinks`: If 'true' you can query links by allStoryblokLinkEntry. The links query lets you create a dynamic navigation tree as it includes also content folders.
- `languages`: An array of strings that will be used in languages request instead of languages in space settings. Use it to only load the languages that you want to.
- `includeDatasources`: If undefined or not defined, fetches all datasources. If an array of specific datasources is provided, only those datasources are fetched. If an empty array, no datasources are fetched.
- `includeTags`: If true, fetches all tags. If false, does not fetch tags.#### How to query all Content Entries
To get all entries unfiltered you can do the following query:
```GraphQL
{
allStoryblokEntry {
edges {
node {
id
name
created_at
published_at
uuid
slug
full_slug
content
is_startpage
parent_id
group_id
internalId
}
}
}
}
```##### How to query filtering of content inside a folder
The following example shows a filter to get all items from a news folder:
```GraphQL
{
allStoryblokEntry(filter: {full_slug: {regex: "/^news\//"}}) {
edges {
node {
name
full_slug
}
}
}
}
```##### How to query filtering of languages
If you use field level translations you can filter for a specific language using following query:
```GraphQL
{
allStoryblokEntry(filter: {lang: {eq: "de"}}) {
edges {
node {
name
full_slug
}
}
}
}
```##### How to query filtering on content type fields
Every field of your content types is available via the prefix `field_`.
This lets you for example to query for a specific component:
```GraphQL
{
allStoryblokEntry(filter: {field_component: {eq: "page"}}) {
edges {
node {
name
full_slug
}
}
}
}
```#### How to query a single content entry
```GraphQL
{
storyblokEntry(slug: { eq: "global-navi" }) {
content
}
}
```#### Datasources
```GraphQL
allStoryblokDatasource {
edges {
node {
id
value
name
data_source
}
}
}
```#### How to query Datasource entries
This will return all datasources, with or not dimensions values:
```GraphQL
allStoryblokDatasourceEntry(filter: { data_source: { eq: "DATASOURCE_SLUG" } }) {
edges {
node {
id
name
value
data_source
data_source_dimension
}
}
}
```If you want to **filter by a specific dimension**, you should use:
```GraphQL
allStoryblokDatasourceEntry(filter: { data_source: { eq: "DATASOURCE_SLUG" }, data_source_dimension: { eq: "DATASOURCE_DIMENSION_VALUE" } }) {
edges {
node {
id
name
value
data_source
data_source_dimension
}
}
}
```#### How to query links
Use the links api to create a dynamic navigation tree. To use this query you need to add `includeLinks: true` in the plugin options.
```GraphQL
allStoryblokLinkEntry {
edges {
node {
id
uuid
slug
parent_id
name
is_folder
published
is_startpage
position
}
}
}
```## The Storyblok JavaScript SDK Ecosystem
![A visual representation of the Storyblok JavaScript SDK Ecosystem](https://a.storyblok.com/f/88751/2400x1350/be4a4a4180/sdk-ecosystem.png/m/1200x0)
## đ Related Links
- **[Storyblok Gatsby.js Technology Hub](https://www.storyblok.com/tc/gatsbyjs)**: Learn how to develop your own Gatsby.js applications that use Storyblok APIs to retrieve and manage content.
- **[Getting Started](https://www.storyblok.com/docs/guide/getting-started?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok)**: Get a project ready in less than 5 minutes.
- **[Storyblok CLI](https://github.com/storyblok/storyblok)**: A simple CLI for scaffolding Storyblok projects and fieldtypes.
- **[Storyblok React.js example demo](https://stackblitz.com/edit/react-sdk-demo)**: See and try how React SDK works with React.js projects
- **[Storyblok Gatsby.js example demo](https://codesandbox.io/s/github/storyblok/getting-started/tree/master/gatsbyjs?fontsize=14&hidenavigation=1&theme=dark)**: See and try how gatsby-source-storyblok works with Gatsby.js projects## âšī¸ More Resources
### Support
- Bugs or Feature Requests? [Submit an issue](/../../issues/new).
- Do you have questions about Storyblok or you need help? [Join our Discord Community](https://discord.gg/jKrbAMz).### Contributing
Please see our [contributing guidelines](https://github.com/storyblok/.github/blob/main/contributing.md) and our [code of conduct](https://www.storyblok.com/trust-center#code-of-conduct?utm_source=github.com&utm_medium=readme&utm_campaign=gatsby-source-storyblok).
This project use [semantic-release](https://semantic-release.gitbook.io/semantic-release/) for generate new versions by using commit messages and we use the Angular Convention to naming the commits. Check [this question](https://semantic-release.gitbook.io/semantic-release/support/faq#how-can-i-change-the-type-of-commits-that-trigger-a-release) about it in semantic-release FAQ.