Take the community feedback survey now.

Jacob Pretorius
Jul 21, 2025
  0
(0 votes)

Build a headless blog with Astro and Optimizely SaaS CMS Part 4 - SSR & Visual Builder Experiences

For those new to the series, you may want to read parts one, two, and three first.

At the end of my last post I mentioned I had two more ideas to explore:

    1. Render some purely Visual Builder content
    2. Render the whole thing dynamically rather than statically.

Let's do that.

From Static to Server-Side Rendered (SSR)

I started a new server-side rendered version of the demo site that builds on everything added in the static site version. This means content changes in the CMS now appear on the SSR website version nearly instantly, without needing to trigger a new build on Vercel. For sites with lots of content changes, this will be a much more suitable approach compared to the webhook process we used with the static site version.

The core change was changing astro.config.mjs from static output to `output: 'server'`. This change tells Astro to stop pre-building all the pages into HTML files and instead render each page on the server when a user requests it.

I also had to install the appropriate Server Adapter, in this case `@astrojs/vercel` since this demo is running on Vercel.

This is great for content-heavy sites where updates are frequent, changes reflect as soon as they are indexed in Graph. 

TypeScript

In the last post, I mentioned that the GraphQL integration could be improved by automatically creating TypeScript types. I thought given that I'm making more changes in the new version, I might as well add that too.

By installing the `@graphql-codegen/cli` package and setting up a codegen.ts config file, we can now run a single command: `npm run codegen`.

This command connects to our Optimizely Graph endpoint, scans our queries in apollo-client.ts, and automatically generates TypeScript types for them. When developing, this means:

  • Full type-safety: No more guessing what fields are available on a content item.

  • Intelligent autocompletion: The code editor knows our schema, which makes writing queries and using the data much faster and less error-prone.

I updated all the page and component layouts to use the imports from our generated types e.g.

import type { StartPage, ArticlePage as ArticlePageType } from '../gql/graphql';

Code editor showing a string type for the article.Heading property

It’s a nice improvement to the developer experience and something you’d want in any real-world project.

Adding Experiences and Visual Builder

The SSR version now properly supports Optimizely's Visual Builder with Experiences.

Optimizely SaaS CMS showing the setup of an Experience Hero component with Description, Heading, and Image properties

I've included three to get started:

  1. ExperienceHero: A simple, reusable full-width hero banner with a background image and text overlay.

  2. ExperienceCarousel: For creating responsive image grids.

  3. ExperienceRTE: A standard Rich Text Editor for displaying any kind of formatted text content.

You can find them in the components folder in the repo, e.g. https://github.com/jacobpretorius/Opti.SaaS.Astro.Demo/blob/main/ssr/src/components/ExperienceHero.astro 

Content editors can now compose these with Experience page type inside the CMS and see them rendered on page.

Optimizely SaaS CMS showing an Experience Page with the three Experience components

Visual builder has come a long way already.

Caching

Graph responses seem to be relatively quick, but as you might expect, a server-side rendered page that has to load page content from an external source for every request won't be as performant as a static site that can instantly start streaming the response HTML.

Thankfully, in-memory caching on the server is still possible. So I added a 2-minute cache for all normal page loads (non-preview mode). Only the first new Graph request will go out to Graph every 2 minutes. You can see and change the cache configuration to your needs in apollo-client.ts.

Conclusion

This new SSR version is a much more robust and dynamic foundation for building with Astro and Optimizely SaaS CMS. It brings the power of instant content updates and a true visual editing experience to the forefront, while making the development process more efficient with type generation.

It upgrades my example from a simple blog (like mine) into something you could use for a more complex and content-rich enterprise site.

Next time, I'll look at integrating the new beta Optimizely content-js-sdk.

Check out the live demo and the code for yourself

See you next time 😉

Jul 21, 2025

Comments

Please login to comment.
Latest blogs
A day in the life of an Optimizely OMVP - Opticon London 2025

This installment of a day in the life of an Optimizely OMVP gives an in-depth coverage of my trip down to London to attend Opticon London 2025 held...

Graham Carr | Oct 2, 2025

Optimizely Web Experimentation Using Real-Time Segments: A Step-by-Step Guide

  Introduction Personalization has become de facto standard for any digital channel to improve the user's engagement KPI’s.  Personalization uses...

Ratish | Oct 1, 2025 |

Trigger DXP Warmup Locally to Catch Bugs & Performance Issues Early

Here’s our documentation on warmup in DXP : 🔗 https://docs.developers.optimizely.com/digital-experience-platform/docs/warming-up-sites What I didn...

dada | Sep 29, 2025

Creating Opal Tools for Stott Robots Handler

This summer, the Netcel Development team and I took part in Optimizely’s Opal Hackathon. The challenge from Optimizely was to extend Opal’s abiliti...

Mark Stott | Sep 28, 2025

Integrating Commerce Search v3 (Vertex AI) with Optimizely Configured Commerce

Introduction This blog provides a technical guide for integrating Commerce Search v3, which leverages Google Cloud's Vertex AI Search, into an...

Vaibhav | Sep 27, 2025

A day in the life of an Optimizely MVP - Opti Graph Extensions add-on v1.0.0 released

I am pleased to announce that the official v1.0.0 of the Opti Graph Extensions add-on has now been released and is generally available. Refer to my...

Graham Carr | Sep 25, 2025