コンテンツにスキップ

StoryblokとAstro

Storyblokはブロックと呼ばれる再利用可能なコンポーネントを作成してコンテンツを管理するコンポーネントベースのヘッドレスCMSです。

このセクションでは、Storyblok integrationを利用してStoryblokとAstroを接続します。

始めるには、以下の手順を行っている必要があります。

  1. Astroプロジェクト - もしAstroプロジェクトをまだ持っていない場合は、自動CLIでAstroをインストールを見ると、すぐに使い始めることができます。

  2. Storyblokアカウントとスペース - もしアカウントを持っていない場合は、無料で登録してスペースを作成します。

  3. Storyblok APIトークン - Storyblokスペースの設定内のアクセストークンタブからAPIトークンを生成できます。

    • Preview token - これは開発中にドラフトもしくは未公開バージョンのコンテンツを取得するために使います。
    • Public token - これは製品版でビルド時に公開済みのコンテンツを取得するために使います。

AstroへStoryblokのクレデンシャルを追加するために、.envファイルをプロジェクトのルートディレクトリに作成して環境変数に追加します。

.env
STORYBLOK_PREVIEW_TOKEN=YOUR_PREVIEW_TOKEN
STORYBLOK_PUBLIC_TOKEN=YOUR_PUBLIC_TOKEN

これで、プロジェクトでこれらの環境変数を利用できます。

ルートディレクトリは以下のように作成したファイル含まれているはずです。

Project Structure
├── src/
├── .env
├── astro.config.mjs
└── package.json

AstroとStoryblokスペースを接続するために、次の中から好みのパッケージマネージャの1つのコマンドを実行して公式のStoryblok integrationをインストールします。

Terminal window
npm install @storyblok/astro

Astro設定ファイルにStoryblokとの接続が含まれるように以下のように修正します。

astro.config.mjs
import { defineConfig } from 'astro/config';
import storyblok from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv(import.meta.env.MODE, process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken:
import.meta.env.MODE === 'development'
? env.STORYBLOK_PREVIEW_TOKEN
: env.STORYBLOK_PUBLIC_TOKEN,
components: {
// Add your components here
},
apiOptions: {
// Choose your Storyblok space region
region: 'us',
},
})
],
});

Storyblokとの接続には以下のプロパティを持つオブジェクトを必要とします。

  1. accessToken - これは、前述で追加したStoryblok APIトークンへの参照です。この例では開発時はpreview tokenを使い、製品版ではpublic tokenを使います。

  2. components - Storyblokのコンポーネント名をローカルコンポーネントへのパスへマッピングするオブジェクトです。StoryblokコンポーネントをAstroへ描画するときに必須となります。

  3. apiOptions - Storyblok API optionsを含むオブジェクトです。

ブロックをAstroへ接続するために、srcディレクトリにstoryblokという名のフォルダーを作成します。このフォルダーには全てのStoryblokのブロックライブラリに対応するAstroコンポネントが含まれます。

例えば、以下のフィールドを持つblogPostというブロックコンテンツがブロックライブラリにある場合を解説します。

  • title - テキストフィールド
  • description - テキストフィールド
  • content - リッチテキストフィールド

目標は、このフィールドを使ってコンテンツに描画するようなAstroコンポーネントを作成する事です。これを実現するために、以下のようにsrc/storyblokディレクトリにBlogPost.astroというファイルを作成します。

src/storyblok/BlogPost.astro
---
import { storyblokEditable, renderRichText } from '@storyblok/astro'
const { blok } = Astro.props
const content = renderRichText(blok.content)
---
<article {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>{blok.description}</p>
<Fragment set:html={content} />
</article>

blokプロパティーにはStoryblokから受信するデータがが含まれます。StoryblokのblogPostブロックで定義したフィールドの値が含まれます。

コンテンツを描画するためには、インテグレーションは以下のようなユーティリティ関数を提供しています。

  • storyblokEditable - Stroyblokでこれらを編集可能にするために、必要に応じて要素へ属性を追加します。
  • renderRichText - リッチテキストフィールドをHTMLに変換します。

ルートディレクトリは以下のように作成したファイル含まれているはずです。

Project Structure
├── src/
│ └── storyblok/
│ └── BlogPost.astro
├── .env
├── astro.config.mjs
└── package.json

最後に、blogPostブロックをBlogPostコンポーネントへ接続するために、Astro設定ファイルのコンポーネントオブジェクトにプロパティを追加します。キーはブロック名で、値はコンポーネントへのパスです。

astro.config.mjs
import { defineConfig } from 'astro/config';
import storyblok from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv(import.meta.env.MODE, process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken:
import.meta.env.MODE === 'development'
? env.STORYBLOK_PREVIEW_TOKEN
: env.STORYBLOK_PUBLIC_TOKEN,
components: {
blogPost: 'storyblok/BlogPost',
},
apiOptions: {
region: 'us',
},
})
],
});

セットアップしたものをテストするために、blogPostコンテンツでtest-postという名前のストーリーをStoryblokで作成します。

Astroでは、以下のコンテンツのtest-post.astroという名前のページをsrc/pagesに作成します。

pages/test-post.astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const storyblokApi = useStoryblokApi()
const { data } = await storyblokApi.get("cdn/stories/test-post", {
version: import.meta.env.DEV ? "draft" : "published",
});
const content = data.story.content;
---
<StoryblokComponent blok={content} />

データ問い合わせをするために、useStoryblokApiフックを利用します。これは連携設定を利用して新しいクライアントインスタンスを初期化します。

コンテンツを描画するために、ストーリーのcontentプロパティをblokとしてStoryblokComponentへ渡します。このコンポーネントはcontentプロパティ内で定義されたブロックを描画します。この例では、BlogPostコンポーネントを描画します。

連携のセットアップが終われば、AstroとStoryblokを使ったブログを作成できます。

  1. Storyblokスペース - 子のチュートリアルでは、新しいスペースを作ることをお勧めします。もしすでにブロックを含むスペースがある場合は、そのまま利用できますが、ブロック名とコンテンツタイプに合わせコードを修正する必要があります。

  2. Storyblokと連携したAstroプロジェクト - 連携するためのセットアップ方法を知るにはAstroとの連携を参照ください。

Blokを作成するためには、Storyblokアプリのライブラリをブロックをクリックします。+ 新規ブロックボタンをクリックして以下のブロックを作成します。

  1. blogPost - 以下のフィールドを持つコンテンツタイプブロックです。

    • title - テキストフィールド
    • description - テキストフィールド
    • content - リッチテキストフィールド
  2. blogPostList - 空のNestable Blokです。

  3. page - 以下のフィールドを持つコンテンツタイプブロックです。

    • body - ネスト可能なBlokフィールド

コンテンツを追加するために、コンテンツタブをクリックしてコンテンツセクションへ移動します。前のステップで作成したブロックライブラリを使って以下のストーリーを追加します。

  1. home - pageブロックを使ったコンテンツタイプを持つストーリーです。bodyフィールド内にはblogPostListブロックを追加します。

  2. blog/no-javascript - ブログフォルダー内のblogPostコンテンツタイプを持つストーリーです。

    title: No JavaScript
    description: A sample blog post
    content: Hi there! This blog post doesn't use JavaScript.
  3. blog/astro-is-amazing - ブログフォルダー内のblogPostコンテンツタイプを持つストーリーです。

    title: Astro is amazing
    description: We love Astro
    content: Hi there! This blog post was build with Astro.

これでコンテンツの準備は整いました。Astroプロジェクトに切り替えてブログの構築を始めましょう。

新しく作成したブロックをAstroコンポーネントへ接続するには、srcディレクトリにstoryblokと呼ばれるフォルダを作成して以下のファイルを追加します。

Page.astroは、pageブロックのbodyプロパティ内の全てのブロックを再帰的に描画するネスト可能なBlokコンテンツタイプのコンポーネントです。また、親要素にstoryblokEditableを追加し、Storyblokでページを編集できるようにします。

src/storyblok/Page.astro
---
import { storyblokEditable } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const { blok } = Astro.props
---
<main {...storyblokEditable(blok)}>
{
blok.body?.map((blok) => {
return <StoryblokComponent blok={blok} />
})
}
</main>

BlogPost.astroblogPostブロックのtitledescriptioncontentプロパティを描画します。

リッチテキストフィールドプロパティのcontentをHTMLに変換するためにrenderRichText関数を利用します。

src/storyblok/BlogPost.astro
---
import { storyblokEditable, renderRichText } from '@storyblok/astro'
const { blok } = Astro.props
const content = renderRichText(blok.content)
---
<article {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>{blok.description}</p>
<Fragment set:html={content} />
</article>

BlogPostList.astroはブログ記事のリストプレビューを描画するNestable Blokコンテンツタイプです。

これはuseStoryblokApiフックを利用してblogPostのコンテンツタイプのストーリー全てを取得します。クエリパラメータのversionを利用して、開発モードではストーリーのドラフトを、製品番ビルドの時は公開されたバージョンを取得します。

src/pages/blogPostList.astro
---
import { useStoryblokApi } from '@storyblok/astro'
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get('cdn/stories', {
version: import.meta.env.DEV ? "draft" : "published",
content_type: 'blogPost',
})
const posts = data.stories.map(story => {
return {
title: story.content.title,
date: new Date(story.published_at).toLocaleDateString("en-US", {dateStyle: "full"}),
description: story.content.description,
slug: story.full_slug,
}
})
---
<h1>My blog</h1>
<ul>
{posts.map(post => (
<li>
<time>{post.date}</time>
<a href={post.slug}>{post.title}</a>
<p>{post.description}</p>
</li>
))}
</ul>

最後に、astro.config.mjsファイルのcomponentsプロパティに作成したコンポーネントを追加します。各キーはStoryblok内のブロック名で、値はsrcからのコンポーネントの相対パスです。

astro.config.mjs
import { defineConfig } from 'astro/config';
import storyblok from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv(import.meta.env.MODE, process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken:
import.meta.env.MODE === 'development'
? env.STORYBLOK_PREVIEW_TOKEN
: env.STORYBLOK_PUBLIC_TOKEN,
components: {
blogPost: 'storyblok/BlogPost',
blogPostList: 'storyblok/BlogPostList',
page: 'storyblok/Page',
},
apiOptions: {
region: 'us',
},
})
],
});

Astroのデフォルト静的モードを利用している場合、動的ルーティングgetStaticPaths()関数を使えます。この関数はビルド時に呼ばれて、ページとなるパスのリストをせいせいします。

src/pages[…slug].astroという以下のファイルを作成します。

src/pages/[...slug].astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
export async function getStaticPaths() {
const storyblokApi = useStoryblokApi()
const { data } = await storyblokApi.get("cdn/stories", {
version: import.meta.env.DEV ? "draft" : "published",
});
const pages = data.stories.map(story => {
return {
params: {
slug: story.full_slug === 'home' ? undefined : story.full_slug
},
props: {
content: story.content
}
}
})
return pages
}
const { content } = Astro.props
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={content} />
</body>
</html>

これはStoryblok APIから取得したスラッグとコンテンツを含む各ストーリーのページを生成します。もしストーリーのスラッグがhomeだった場合、undefinedのスラッグを返し/ルーティングを生成します。

プロジェクトでSSRを有効にする場合、Storyblokデータを取得するために動的ルーティングで slug パラメータが利用されます。

src/pages/[...slug].astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const storyblokApi = useStoryblokApi()
const slug = Astro.params.slug === undefined ? "home" : Astro.params.slug
let content;
try {
const { data } = await storyblokApi.get(`cdn/stories/${slug}`, {
version: import.meta.env.DEV ? "draft" : "published",
});
content = data.story.content
} catch (error) {
return Astro.redirect('/404')
}
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={content} />
</body>
</html>

このファイルは動的なslugパラメータと一致するページデータをStoryblokから取得して描画します。 もし見つからない場合は、404ページへリダイレクトします。

ウェブサイトをデプロイするために、デプロイガイドへアクセスして好みのホスティングプロバイダーにあわせた説明に従ってください。

もしプロジェクトがAstroのデフォルトである静的モードを使っている場合、コンテンツを変更した時に新しいビルドを行うトリガーをするためのWebhookをセットアップする必要があります。もしNetlifyかVercelをホスティングプロバイダーとして使っている場合、コンテンツイベントから新しいビルドをトリガーするためにWebhook機能を使えます。

NetlifyのWebhookをセットアップするためには以下の手順が必要です。

  1. ダッシュボードに行き、Build & deployをクリックします。

  2. Continuous Deploymentタブから、Build hooks セクションを探しAdd build hookをクリックします。

  3. Webhookの名前を指定してビルド時にトリガーされるブランチを選択します。Saveをクリックし生成されたURLをコピーします。

VercelのWebhookをセットアップするためには以下の手順が必要です。

  1. ダッシュボードへ行き、Settingsをクリックします。

  2. Gitタブから、Deploy Hooksセクションを見つけます。

  3. Webhookの名前を指定してビルド時にトリガーされるブランチを選択します。Addをクリックして生成されたURLをコピーします。

StoryblokスペースのSettingsから、Webhooksタブをクリックします。Story published & unpublishedボックスにWebhook URLをペーストします。保存を押してWebhookを作成します。

これで、新しいストーリーを公開しても、新しいビルドがトリガーされブログが更新されます。

  • StoryblokはプロジェクトにStoryblokを追加する Astro Integrationを提供しています。
  • 追加してください!

その他のCMSガイド