How to personalize Storyblok content based on visitor behavior


Two people land on your homepage in the same minute. One arrived from a Google Ads campaign, on mobile, for the first time. The other is a returning visitor who has already viewed your pricing page twice and abandoned a cart yesterday.

Your Storyblok website serves them the exact same hero banner.

That's not a flaw in Storyblok. It's a headless CMS, and it's very good at what it does: component-based content, clean APIs, a friendly editing interface, and the scalability to support content-rich websites without a monolithic backend.

But Storyblok doesn't natively support personalized content based on user behavior or demographics. It provides static content blocks without dynamic audience segmentation or real-time personalization options.

So if you want that returning cart-abandoner to see something different from the first-time paid-traffic visitor, you have to add that capability yourself. There are two realistic ways to do it, and they differ enormously in cost, timeline, and who ends up owning the work.

Personalize your content based on the user context

Create and publish personalized experiences without replacing your CMS or over-relying on your developers.

First: what counts as visitor behavior?

Before comparing implementations, it helps to be concrete about what you'd actually target. Personalization projects fail more often from vague segmentation than from bad engineering.

In practice, most teams end up wanting some combination of these signals:

  • Acquisition context

    Where the visitor came from. Campaign medium, source, campaign name, or ad content. A visitor from a paid search campaign about a discount should probably not see the same headline as someone arriving from organic search.

  • Session and journey

    Is this the first session or the fifth? What page did they land on? What referrer sent them? Have they already viewed the pricing page?

  • Returning vs. new

    One of the highest-value and simplest splits available. A returning visitor doesn't need the same explanatory copy as a stranger.

  • Technology

    Device category, OS, vendor. Mobile visitors have different attention spans and different form tolerance.

  • Location

    City, state, country, population, demographics. Useful for pricing, currency, shipping promises, and regional social proof.

  • Declared or inferred interest

    Someone who has repeatedly engaged with one product category shouldn't be pitched a generic value proposition.

  • Purchase intent

    Cart contents, cart value, abandonment history, completed goals, order count. A visitor with an abandoned cart worth $200 is a different person from one with an empty cart.

These are the segments that show up over and over in real deployments. Keep them in mind as you read the two options below, because the real cost difference isn't building one segment. It's building the fiftieth, and then changing it three weeks later.

Segment visitors based on conversion intent

Option 1: Build personalization in-house

This is the path most engineering teams instinctively reach for, and there are well-written public tutorials showing exactly how to do it, including a technical guide on the Netlify blog that walks through personalizing a Next.js website with Storyblok.

It works. It's also more expensive than it looks on day one.

What you actually have to build

The hard way to build personalization involves writing custom middleware, manually handling cookies, and hard-coding logic to swap components based on user segments. Broken down, that means:

  1. A data collection layer

    Behavioral targeting requires behavioral data. You need to capture page views, sessions, referrers, campaign parameters, device information, and custom events like conversions or cart updates. Then you need to store them somewhere queryable and persist identity across sessions and devices.

  2. A profile store

    Raw events aren't segments. You need something that aggregates events into a durable visitor profile (like session counts, interests, order history, abandonment history) and can answer questions about a specific visitor in milliseconds, at request time.

  3. A segmentation engine

    Somewhere, the rule "returning mobile visitor from a paid campaign who viewed pricing" has to be expressed and evaluated. Built in-house, this typically starts as hard-coded conditionals and slowly turns into a homegrown rules DSL that only one engineer fully understands.

  4. Request-time delivery

    Custom middleware to intercept requests and check cookies, if/else logic to determine which version of a page to serve, and user personas defined directly in the code. You'll also need to solve caching (remember that personalized responses and CDN caching are genuinely difficult to reconcile) and decide between server-side rendering (fast, no flicker, harder) and client-side swapping (easy, but visitors watch the original content flash before it's replaced).

  5. Content modeling for variants

    Your Storyblok blocks now need somewhere to hold n versions of themselves, plus fallback logic for visitors who match no audience or match several.

  6. Analytics

    Personalization without measurement is decoration. You need experiment assignment, exposure tracking, conversion attribution, and a statistics engine that can tell you whether a lift is real or noise.

  7. An interface for the people who actually do marketing

    This is the step that quietly gets cut from every in-house project plan, and it's the one that determines whether the system gets used.

The real cost

The build cost alone is high, but the maintenance cost is what compounds.

If the marketing team wants to add a new segment or change a rule, they have to ask a developer to rewrite the logic and redeploy the application.

Think about what that means at velocity. A marketer with ten hypotheses to test now needs ten tickets, ten code reviews, and ten deploys. In practice, they'll test two, wait three weeks each, and stop bothering.

This is the exact trap teams walk into after launch: the marketing team becomes dependent on developers for everything.

You're also signing up to own a system that is not your core product. Every dependency upgrade, every edge case in bot traffic, every privacy regulation change, every "why did this visitor see the wrong banner" investigation is now your engineering team's problem, forever.

When it's still the right call

To be fair, in-house is defensible when personalization logic is your competitive moat (a recommendation engine you're selling), when regulatory constraints forbid third-party data processing entirely, or when you already run a mature experimentation platform, and Storyblok is just one more surface plugged into it.

For most teams building a marketing site, none of those apply.

Option 2: Use the Optimize app (recommended)

Optimize is a Storyblok app that brings personalization and AB testing directly into the Storyblok interface. It lets you connect any block to a slot, so marketers can manage dynamic content without touching code.

The distinction that matters: this isn't a library your developers wire into every component. It's an app that runs inside Storyblok, where the content already lives.

Low developer's involvement

You can integrate Croct into your Storyblok project by running a single command line:

croct init

That's the entire developer scope. Not "that's phase one". That's the scope.

We claim this in the most direct way available: we took the hand-coded Netlify/Storyblok personalization demo and rebuilt it.

To switch to Croct, we didn't add any code. We deleted it, removing the custom middleware, the cookie parsing logic, and the manual component swapping. The croct init command added the SDK and changed a single line in the original project. By initializing Storyblok with Croct, the application automatically gained server-side personalization capabilities.

Two things worth flagging for engineers evaluating this:

  • The integration runs on the server side, ensuring high performance and eliminating the flicker effect often associated with client-side solutions. No visible content swap, no layout shift, no penalty to your Core Web Vitals.
  • It's a meaningfully different approach from the alternatives in the ecosystem. Storyblok's VWO plugin was a step forward, but the implementation still required significant coding for each content block, keeping marketing teams dependent on developers for every new block integration, and other plugins often require manually adding fields to every block you want to test.

Day-to-day workflow

Once the app is installed and your space is connected, the workflow lives entirely in Storyblok.

  1. Mark a block as dynamic

    A dynamic block is a block that has the Croct plugin field enabled. Open the Optimize app tab in the left menu under Apps, open the Dynamic blocks area, find the block you want, and toggle it on.

  2. Connect the block to a slot

    Open your Storyblok story, find the block, open its settings, and locate the Optimize field. Use the dropdown to select the slot you want to connect, or create a new one, and save.

  3. Let existing content be the safety net

    Once connected, your Storyblok content acts as the default and fallback for visitors not targeted by any active experience, while Croct automatically injects dynamic content for those who match your audience criteria. Nothing breaks for unmatched visitors, and nothing breaks if an experience is paused.

  4. Define audiences without code

    Instead of hard-coded personas, marketers write conditions directly user is returning, campaign's medium is "cpc", device's category is "mobile", user has viewed a page with path "/pricing", and many others.

  5. Ship variations and measure them

    The app gives you dashboards with traffic, engagement, and conversion metrics, profiles listing visitors with their attributes and event timelines, audiences, dynamic blocks, slots, and experiences, covering both personalized experiences and AB tests with tools to create, manage, and analyze performance.

All of it happens without ever leaving the Storyblok interface. No tab-switching between your CMS and a separate optimization platform to configure a test or read a result.

On the statistics side, the AB testing is Bayesian, which enables more efficient tests with greater confidence and statistically significant results, helping you optimize more quickly and with less traffic. That last part matters more than it sounds: traffic volume is the binding constraint on most experimentation programs.

Gradual adoption

This is the argument that usually closes the deal with a skeptical engineering lead: the app adds dynamic content for experimentation and optimization directly to your existing blocks, so you can use it only for the elements and components you want to experiment with.

Say the top menu, main banners, and forms are the most important elements for conversion on your site. Those could be dynamic elements integrated with Croct for AB testing or personalized experiences, while the rest of the website still lives in Storyblok.

You can gradually connect elements and components between the two, giving content and growth teams the autonomy they need. Start with one hero banner. Measure it. Expand if it works.

Everything for conversion optimization

From personalization and experimentation to content and data management, we have all you need to deliver better user experiences.

Side-by-side comparison

Build in-houseOptimize app
Time to first personalized blockWeeks to monthsMinutes of developer time, then minutes per experience
Developer effort to launchMiddleware, cookie handling, segmentation logic, variant modeling, analyticsOne command: croct init
Adding a new audienceCode change, review, deployWritten in the app by a marketer
Who owns experimentationEngineering, permanentlyMarketing, after initial setup
RenderingYour choice (and your problem to get right)Server-side, no flicker
Analytics and statisticsBuild or integrate separatelyBuilt in, Bayesian, inside Storyblok
Ongoing maintenanceYours foreverVendor's
Adoption riskHigh (systems too slow to use get abandoned)Low (the workflow lives where content already lives)

The recommendation

I know I am biased, but the difference is pretty obvious, right?

Build in-house if personalization logic is genuinely your product, or if a hard regulatory constraint rules out third-party processing. Otherwise, use the app.

The reasoning isn't primarily about cost, though the cost difference is large. It's about who ends up holding the work.

An in-house build converts every future marketing hypothesis into an engineering ticket. And the tax on that arrangement isn't the tickets you file, it's the experiments nobody bothers to propose because the process is too slow to be worth it.

Once a developer runs that single command, the marketing team has full control: they can run as many AB tests as needed or create complex personalization experiences without touching the source code again.

Developers should focus on building great products, not maintaining personalization logic, and you can have a high-performance, server-side personalized site without inheriting the technical debt.

To get started, pick your single highest-traffic conversion element, install the app, run croct init, toggle that block to dynamic, connect it to a slot, and build one audience (user is returning is a good first test). Measure it for a couple of weeks. Then decide how much further to go.

Full setup instructions are in the Storyblok integration guide, the app itself is on the Storyblok marketplace, and the refactored demo repository shows the before-and-after against a hand-coded implementation if you want to read the diff before committing.

Let's grow together!

Learn practical tactics our customers use to grow by 20% or more.

By continuing, you agree to our Terms & Privacy Policy.