For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.
Contentful
Amplitude 実験用のContentfulプラグインを使用すると、Contentfulでコンテンツのバリエーションを作成したり、実験を使用して各ユーザーがどのバリエーションを表示するかを制御したり、それらのバリエーションのパフォーマンスを追跡したりできます。
機能
- Amplitude 実験でA/Bテストを実行し、Contentfulでコンテンツを作成します。
- Amplitude 実験からプロパティを読み取り(5秒ごとに更新されます)、それらのプロパティを中心にコンテンツを作成します。
要件
このプラグインを使用するには、次のものが用意されていることを確認してください。
- Amplitude 実験が有効になっているAmplitudeプランへのアクセス。
- Amplitude OrgのURL値。この値は、Amplitudeへのアクセスに使用するURLに表示されます:
https://app.amplitude.com/experiment/<ORG_URL>/dashboard。 - Management APIキー。これは実験のサイドバーにあります。
インストールと使用
ContentfulとAmplitudeで以下の手順を実行して、Amplitude 実験用のContentfulプラグインを追加して有効にします。
プラグインをインストールする
- Contentfulマーケットプレイスからプラグインをインストールします。
- プラグイン設定で、実験で作成したデータセンター、組織URL、およびManagement APIキーを入力します。
- 選択した環境にインストールをクリックします。
- 保存をクリックしてプラグインの設定を完了します。
プラグインを有効にすると、Variant Containerコンテンツモデルがコンテンツモデルタブに表示されます。
コンテンツモデルにバリアントコンテナを追加する
ContentfulのバリアントコンテナはAmplitude 実験と連携して、各ユーザーがどの実験バリアントを表示できるかを決定します。
最良の結果を得るためには、Amplitudeは実験を行う際に「参照」コンテンツタイプを使用することをお勧めします。
バリアントコンテナを追加するには
- バリアントコンテナを追加するページのコンテンツモデルを開きます。
- **「フィールドを追加」**をクリックします。[参照]フィールドを選択します。
- フィールドの名前を入力します。 最良の結果を得るには、フィールドの目的に合った名前を入力してください。 たとえば、
Hero BannerまたはDemo CTAです。 - タイプとして1つの参照を選択します。
- 追加して設定をクリックします。
- フィールド設定の「名前とフィールドID」タブで、指定されたエントリタイプのみを受け入れるを選択し、バリアントコンテナを選択します。この設定により、Contentfulがページコンテンツを配信する場合に Contentful API レスポンスの一貫性が保たれます。
- 確認をクリックしてフィールドを作成し、保存をクリックしてコンテンツモデルを更新します。
実験にコンテンツを追加する
バリアントコンテナと参照フィールドを設定したら、実験を追加したいページを開きます。
実験にコンテンツを追加するには
- コンテンツエディタで、ページを選択します。
- 前の手順で作成したフィールドを見つけ、関連付けられた**「コンテンツを追加」**セレクターをクリックします。
- 追加するコンテンツとして「バリアントコンテナ」を選択します。
- フィールド設定で、実験のフラグキーを入力します。 [フラグキー]フィールドには、入力時に一致するキーが表示されます。
- フラグキーを選択すると、そのキーに関連付けられたすべてのバリアントが「バリアント」セクションに表示されます。
- 各バリアントに対して、[既存のエントリをリンクする]または[新しいコンテンツタイプを作成する]を選択して、バリアントにデータを入力します。
- 「公開」をクリックしてバリアントコンテナを公開します。
- 公開をクリックして、実験を有効にした状態で更新されたページを公開します。
フロントエンドとの統合
Contentful はユーザーがページをリクエストすると JSON を返します:
{
"__typename": "PageLanding",
"sys": {
"id": "2cayfg7wVF5WezADCHgSgL",
"spaceId": "4y4crvvoco9a"
},
"internalName": "Homepage",
"heroBanner": {
"__typename": "VariationContainer",
"experiment": {
"id": "183980",
"key": "contentful-hero-banner",
"name": "contentful-hero-banner",
"tags": [],
"state": "running",
"deleted": false,
"enabled": true,
"endDate": null,
"decision": null,
"variants": [
{
"key": "control"
},
{
"key": "treatment"
}
],
"projectId": "289220",
"startDate": "2024-02-22",
"deployments": ["14"],
"description": "",
"bucketingKey": "amplitude_id",
"bucketingSalt": "28fWNw1M",
"bucketingUnit": "User",
"decisionReason": null,
"evaluationMode": "remote",
"experimentType": "hypothesis-testing",
"rolloutWeights": {
"control": 1,
"treatment": 1
},
"targetSegments": [],
"stickyBucketing": false,
"rolledOutVariant": null,
"rolloutPercentage": 0
},
"experimentId": "contentful-hero-banner",
"meta": {
"control": "6rmYK8YKYtTkKcFRY9Pf2w",
"treatment": "kwDjI2f2vKE2GvQoeqq1d"
},
"variationsCollection": {
"items": [
{
"__typename": "Hero",
"sys": {
"id": "6rmYK8YKYtTkKcFRY9Pf2w",
"spaceId": "4y4crvvoco9a"
},
"preHeadline": "Organic Products",
"headline": "100% Fresh Food",
"cta": "Shop Now",
"description": "Whatever you want from your local stores, brought right to your door. \t\t\t\t\t\t\t",
"image": {
"__typename": "Asset",
"sys": {
"id": "6PkraSxWWd96AU6FTYVssd"
},
"title": "Fresh food",
"description": "",
"width": 2560,
"height": 960,
"url": "https://images.ctfassets.net/4y4crvvoco9a/6PkraSxWWd96AU6FTYVssd/69b8d7f7cabb9f578097d50f2bf8aa70/Hero-3-1-scaled.jpg",
"contentType": "image/jpeg"
}
},
{
"__typename": "Hero",
"sys": {
"id": "kwDjI2f2vKE2GvQoeqq1d",
"spaceId": "4y4crvvoco9a"
},
"preHeadline": "Exclusive Offer",
"headline": "Loyalty Program",
"cta": "Get Free Shipping",
"description": "We missed you! Finish your order today and get free shipping when you join our loyalty program.",
"image": {
"__typename": "Asset",
"sys": {
"id": "6WFOK0460CwrW7aChl1QjM"
},
"title": "Loyalty Green",
"description": "",
"width": 1920,
"height": 720,
"url": "https://images.ctfassets.net/4y4crvvoco9a/6WFOK0460CwrW7aChl1QjM/e52fb2129b848203f6006ff9881309d9/Hero-_-loyalty-green.jpg",
"contentType": "image/jpeg"
}
}
]
}
}
}
experimentオブジェクトには、実験に関する有用なメタデータが含まれています。フロントエンドをレンダリングして実験を含めるには、metaオブジェクトとvariationsCollectionオブジェクトを使用します。Amplitude 実験はバリアント識別子を渡し、それをmetaオブジェクト内のオプションと照合します。バリアントを設定した後、次のことが可能です:
variationsCollectionオブジェクト内のアイテムを反復処理し、一致するIDを持つバリエーションを表示します。- 配列の検索を避けるために、バリアントIDを使用してContentfulに直接呼び出してください。
詳細については、次の React と TypeScript の例を参照してください。
import React, { useEffect, useState } from "react";
import { Experiment } from "@amplitude/experiment-js-client";
import sdk from "contentful-sdk";
// --- Types (adjust to your schema) ---
type Hero = {
__typename: "Hero";
sys: { id: string };
// ...other fields you render
};
type HeroBanner = {
experimentId?: string; // key you use in Amplitude Experiment
meta?: Record<string, string>; // maps variant key -> variation id
variationsCollection?: { items: Array<Hero | null | undefined> };
};
// --- Experiment init ---
const CLIENT_KEY =
process.env.NEXT_PUBLIC_AMPLITUDE_EXPERIMENT_CLIENT_KEY ?? "";
export const experiment = Experiment.initialize(CLIENT_KEY, {
// Only enable verbose logging in dev if you like:
debug: process.env.NODE_ENV !== "production",
});
// --- Component hook snippet ---
export function useHeroVariant(userId: string | undefined) {
const [hero, setHero] = useState<Hero | null>(null);
useEffect(() => {
// Skip until we have a user id
if (!userId) return;
let isMounted = true;
(async () => {
try {
// 1) Fetch variants for this user
await experiment.fetch({ user_id: userId });
// 2) Load the banner/experiment mapping from Contentful
const heroBanner = await sdk.getEntry<HeroBanner>("ENTRY_ID_HERE");
if (!heroBanner) return;
// 3) Resolve the variant key from the experiment
const experimentKey = heroBanner.experimentId ?? "control";
const { value: variantKey } = experiment.variant(experimentKey);
if (!variantKey) return;
// 4) Map the variant key -> variation id through Contentful metadata
const variationId = heroBanner.meta?.[variantKey];
if (!variationId) return;
// 5) Find the matching Hero item
const match =
heroBanner.variationsCollection?.items.find(
(item): item is Hero =>
!!item &&
item.__typename === "Hero" &&
item.sys?.id === variationId,
) ?? null;
if (isMounted) setHero(match);
} catch (err) {
// Consider forwarding to your logger
console.error("Failed to resolve hero variant", err);
if (isMounted) setHero(null);
}
})();
return () => {
isMounted = false;
};
}, [userId]); // Note: don't include heroBanner (it's local) or setHero
return hero;
}
ユーザーがexperiment.variant()の戻り値としてoffを受け取るケースも考慮にいれます。
これは役に立ちましたか?