When I published Release on Demand, I outlined a framework for shipping software with precision—delivering code continuously while choosing exactly when to activate functionality. But when you're building statically-generated websites, how do you retain that dynamism?
This post dives into the core problem of reconciling feature flags with SSG—a challenge we faced while building corewellhealth.org, a JAMstack-powered static site designed for performance, safety, and continuous delivery.
With static site generation, content is baked into HTML during build time. But feature flags are inherently dynamic—they change at runtime. So when we ship a static site, what state should the feature flags be in?
Options we initially explored:
Neither option was good enough. Our release on demand model demanded more agility.
We started by embedding our feature flag system into our CMS and fetching flags on the client. The initial paint used default flag states hardcoded in our React context.
This improved things—users got an immediate render, and updated flag states would take over asynchronously. But a mismatch between the default state and the actual flag state led to UX bugs. For example:
Search is on by default in the static build. Suddenly, our backend search service goes down. We toggle the flag off, but users still see and interact with the feature before their client gets the updated flags.
We needed the static default to sync with the current flag state. But that required a rebuild. And rebuilding meant a PR. Unless...
Our solution: Integrate the feature flags into the SSG build process itself.
What we were missing was the ability to automatically resync the static build with the current flag state. This meant we could:
This approach also addressed another problem for us inherently: because of our GraphQL integration with the static builds, if our code didn't match our flags, it would break the build and fail. While this was a happenstance, it effectively acted as a form of type-safety for feature flags.
Check out github.com/open-feature/cli for a new project we're working on to actually generate type-safe feature flag clients!
Here’s how we made it work:
This meant:
This hybrid solution gave us the best of both worlds:
Even our Change Advisory Board gained confidence. Feature toggles became a formal mechanism for release control—with traceability, safety, and minimal blast radius.
I've created a Next.js example repo that demonstrates this pattern. You can find it here:
Release on Demand with SSG Example
This repo shows how to:
getStaticProps
or getStaticPaths
for static generation.useEffect
).Statically-generated sites and feature flags don’t have to be at odds. By embedding flag state at build time, rehydrating with runtime updates, and triggering fresh static builds on flag changes, we bridged the gap between static delivery and dynamic behavior.
This pattern isn’t just a workaround—it’s a powerful enabler for truly modern web delivery.
Have questions or want to dive deeper? I’d love to chat CNCF Slack. Let’s keep the conversation going!
2025-04-06