Help teams manage content creation and approval in a clear and structured way
Hygraph
Docs

#Set up personalization with Hygraph's Variants and frontend logic

This guide is ideal for anyone who’s new to personalization or looking to experiment with simple, practical ways to tailor content. Whether you’re exploring how to deliver more relevant interactions or just want to test out basic strategies, you’ll learn how to use Hygraph’s Variants together with frontend logic to create targeted content for different audience groups.

To get started, you can use the data you already have, such as purchase history, location, or browsing behavior, to create simple rules for personalization. This information could come from targeted email segments, dynamic website banners, or purchase and browsing history. Implementing personalization without investing in any additional tools shows its benefits and helps you see what resonates with your audience.

#Use cases

Personalized content helps improve engagement, relevance, and conversion rates. You can personalize content for your users in several ways.

#Campaign segmentation

Show specific content to users depending on the campaign or referral link that brought the user to your website.

Setup

  1. Set up tracking parameters in your campaign URLs.
  2. Define logic that matches each campaign to the right content.
  3. Display targeted content or offers when users land on your site.

Example

If you’re running two coffee bean campaigns, one on Instagram aimed at home baristas and another on Etsy targeting coffee shop owners, you can determine which content to show based on their needs.

#User input

Collect your users' preferences when they arrive at your website.

Setup

  1. Prompt visitors to select the role or category they identify with when they arrive on your website.
  2. Store their answer in a persistent cookie or session variable.
  3. Use this stored preference to serve relevant content across pages and future visits.

Example

A visitor who selects “Coffee shop owner” will continue to see wholesale offers and bulk order options, while a “Home barista” might see guides and starter kits.

#User profile

If users have accounts or a history of past purchases, you can utilize that data to recommend and display personalized content tailored to their preferences and behavior.

Setup

  1. Collect information, such as preferences and demographics, from your users' accounts.
  2. Leverage purchase history or browsing behavior to recommend relevant products.
  3. Update content dynamically as the user’s activity evolves.

Example

A returning customer who previously purchased dark roast beans might be shown new arrivals in similar flavors or bundle deals with grinders.

#How it works

You can deliver personalized content to your users using Hygraph and your frontend logic.

  1. Identify the user’s role. Use your own frontend logic to determine a user’s role. This step does not require a Customer Data Platform (CDP).
  2. Map the role to a segment and variant in Hygraph. Each role corresponds to a segment in Hygraph, and each segment is linked to a specific content variant. This setup allows you to deliver the right content automatically, without relying on a separate personalization engine.

#Setup in Hygraph

  1. Enable Variant support for fields in your schema. You can enable variants for as many fields from the base content entry, per your requirement. It is not necessary to mark all schema fields as variant-enabled fields.

    • For the Article schema, we have enabled variant support for the Title, Summary, Content, and Picture fields.

  2. Add Segments. In this example, we have two segments:

    • Segment Coffee Shop Owners with slug shops

    • Segment Home Baristas with slug home

  3. Add Variants of the base entry and link it to Segments.

    • The base entry Understanding Coffee Bean Origins includes two variants, one for the Coffee Shop Owners segment and the other for the Home Baristas segment.

#Display personalized content

#Overview

The following is a high-level overview of the steps that you need to run through to display personalized content to your users:

  1. Retrieve the segment from the search parameters, a cookie, or the user’s profile via the customers database.

  2. Extract the article id parameter from the route or request object.

  3. Fetch the article by ID and its variants that are linked to the segment.

  4. If an article is found, overlay the variant fields on top of the base article. If no variant is found, return the article as is. If no article is found, throw an error.

    const segment = await getSegment(searchParams);
    const { id } = await params;
    let article = await getArticleById(id, segment);
    if (!article) {
    return {
    title: "Article Not Found",
    description: "The requested article could not be found.",
    };
    }
    article = applyVariant(article);

#Step-by-step process

  1. Retrieve the segment from the search parameters or cookies.

    export async function getSegment(
    searchParams: Promise<{ segment?: string }>,
    ): Promise<string | undefined> {
    const { segment } = await searchParams;
    const cookieStore = await cookies();
    const cookieSegment = cookieStore.get("segment");
    return segment || cookieSegment?.value;
    }
  2. Query the variant content, and pass the segment slug as a filter.

    // Query article including variants
    export type ArticleType = {
    id: string;
    title: string;
    summary: string;
    variants: {
    title: string;
    summary: string;
    content: {
    raw: string;
    html: string;
    };
    picture: {
    thumbnailUrl: string;
    url: string;
    };
    }[];
    content: {
    raw: string;
    html: string;
    };
    picture: {
    thumbnailUrl: string;
    url: string;
    };
    createdAt: string;
    };
    // Get a single article by ID
    export async function getArticleById(
    id: string,
    segment?: string,
    ): Promise<ArticleType | null> {
    const query =
    `
    query ArticleByIdQuery($id: ID!, $segment: String) {
    article(where: {id: $id}) {
    id
    title
    summary
    content {
    raw
    html
    }
    picture {
    thumbnailUrl: url(
    transformation: {image: {resize: {width: 800, height: 600}}
    document: {output: {format: webp}}}
    )
    url: url(
    transformation: {image: {resize: {height: 1440}}
    document: {output: {format: webp}}}
    )
    }
    createdAt
    variants(where:{
    segments_some:{
    slug: $segment
    }
    }) {
    title
    summary
    content {
    raw
    html
    }
    picture {
    thumbnailUrl: url(
    transformation: {
    image: { resize: { width: 800, height: 600 } }
    document: { output: { format: webp } }
    }
    )
    url: url(
    transformation: {
    image: { resize: { height: 1440 } }
    document: { output: { format: webp } }
    }
    )
    }
    }
    }
    }
    `;
    try {
    const data = await fetchFromCMS(query, { id, segment });
    return data.article;
    } catch (error) {
    console.error(`Error fetching article with ID ${id}:`, error);
    return null;
    }
    }
  3. Now, merge the article values with variant values. The following code snippet builds a single data object by combining:

    • All the base article fields, and
    • All the fields of its first variant, with variant fields overriding article fields if there’s a conflict.
    // Merges a variant onto the base entry
    export function mergeVariant<T extends Record<string, any>>(
    data: T,
    variant?: Partial<T>,
    ): T {
    if (!variant) {
    return data;
    }
    return {
    ...data,
    ...variant,
    };
    }
  4. Now, pick the first merged variant, and display it to your user.

    // Pick the first merged variant
    export function applyVariant<
    T extends Record<string, any> & { variants: Array<Partial<T>> },
    >(data: T): T {
    return mergeVariant(data, data.variants[0]);
    }