Have you ever faced a situation where you have a standalone Umbraco site and want to add some advanced interactivity, but dread the thought of wrestling with complex vanilla JavaScript?
Perhaps you need a widget with intricate state requirements that has outgrown the capabilities of lightweight libraries like AlpineJS.
Or maybe you have a front-end team that are absolute wizards with React and can craft awesome user experiences, but going with a headless CMS and full-blown React app feels like overkill.
In this article, we’ll explore how the Island Architecture pattern can inspire us to introduce pockets of React within your existing Umbraco site.
We’ll cover how to get set up with React and discuss some key considerations to keep in mind if you’re exploring this approach.
The Islands Architecture Pattern
Islands Architecture is a design pattern that optimises web rendering by loading interactive components only where they’re needed - like islands in a sea of static content. This approach originated from the need to improve performance in modern web applications, enabling developers to embed dynamic, interactive elements within otherwise static pages.
You might be wondering, “Isn’t Umbraco traditionally a server-rendered CMS? How does the islands concept fit here?”.
While the purest form of the Islands Architecture is often associated with front-end meta frameworks and static site generators, the core principles can be adapted to enhance an Umbraco site.
It’s important to clarify that this isn’t about implementing a traditional islands setup as seen in frameworks like Astro. Instead, we’re borrowing the concept to introduce React components into specific parts of Umbraco pages.
You may have used similar techniques before to add small, interactive React widgets to a site, but without giving it a formal name.
Here we are formalising the process somewhat and directly acknowledging the Islands Architecture pattern as our inspiration.
Setting Up React Islands in Umbraco
In this example setup, we’ll use an Umbraco site alongside a front-end project containing our styles and React setup, leveraging TypeScript.
A complete working example can be found on GitHub here
Umbraco CMS
On the backend, we’re working with a plain v13 Umbraco install, along with the Clean Starter Kit to provide a foundation and some content to work with.
Front-End Dependencies
To start, we’ll install the front-end dependencies. For this example, we’re using Tailwind for styles and Vite for compiling and bundling our assets.
The dependencies are already configured in the GitHub repo, but if you are starting from scratch, you will need the following:
In order to process this and any other React islands, we are going to create a top-level orchestrator to collect all the .react-component elements (defined by the class), then initialise them based on data-component attribute.
In frontend/scripts/main.tsx
import { createRoot } from "react-dom/client";
import HelloIsland from "@components/HelloIsland";
//import AnotherComponent from "AnotherComponent/AnotherComponent";
type ComponentMap = {
// 'any' used for demo purposes
// For better type safety, consider using a mapped type with specific prop definitions per component.
[key: string]: (props: any) => JSX.Element;
};
const componentsMap: ComponentMap = {
HelloIsland,
//AnotherComponent,
// Add more components as needed
};
document.addEventListener("DOMContentLoaded", () => {
// Select all elements that should host a React component
const elements = document.querySelectorAll(".react-component");
elements.forEach((element) => {
const componentName = element.getAttribute("data-component");
if (!componentName) return;
const Component = componentsMap[componentName];
if (!Component) return;
// Render the component into the element
const root = createRoot(element);
root.render(<Component />);
});
});
We're using the main.tsx entry point for demo purposes here
Additional components can be registered in the orchestrator and added to the componentsMap as needed.
Bundling up the JavaScript for Umbraco
We now need to configure Vite and an NPM script to bundle up our JavaScript and copy it to our Umbraco project.
Run the following to build the assets: npm run build:assets
Now, your React components are bundled and optimised alongside Umbraco's assets and will get our 'Hello Island!' example rendering in Umbraco wherever we put the island definition:
You can compose your JSON data however you see fit in your existing Umbraco setup. For this simple demo example, we'll just do it in the Razor:
@{
var content = new {
articles = pageOfArticles.Select(x => new { x.Name, x.Subtitle })
};
var contentJson = Newtonsoft.Json.JsonConvert.SerializeObject(content);
}
You can find the above code in-situ in the latestArticlesRow.cshtml file in the repo here.
We can now extend our orchestrator to accept the content and pass to the React component as needed.
const elements = document.querySelectorAll(".react-component");
elements.forEach((element) => {
// Get the component name from the data attribute
const componentName = element.getAttribute("data-component");
if (!componentName) {
console.error('Attribute "data-component" is missing or null.');
return;
}
const Component = componentsMap[componentName];
if (!Component) {
console.error(`Component "${componentName}" not found in componentsMap.`);
return;
}
// Extract content props from data-content attribute
const contentData = element.getAttribute("data-content");
const content = contentData ? JSON.parse(contentData) : {};
// Render the component and pass content props
const root = createRoot(element);
root.render(<Component {...content} />);
});
The full code above can be found in the repo here.
Within the BlogListing.tsx React component, we can then define TypeScript types for the incoming content:
Now, your React component receives dynamic content directly from Umbraco. 🙌
Working Example
As mentioned above, there is a working example in the GitHub repo of the BlogListing.
In the interest of keeping it festive, the Blog Listing in the demo uses an Aceternity UI component to create and animated shooting star effect on the list cards ✨
Clean Starter Kit with Aceternity 'Meteors' React component used for the blog listing
In reality, you would only want to consider this for more complex use cases, maybe something with complex state or interactions, but it makes for a fun example.
Why Use React Islands in Umbraco?
So, why go to the effort of integrating React into Umbraco in this way?
Selective Enhancement
This approach allows you to create "islands" within your site - dedicated areas for interactive features like widgets, calculators, or dynamic forms - powered by React, while leaving the rest of the Umbraco framework intact. It’s an ideal solution for projects that don’t require a full separation of frontend and backend but still have specific needs for advanced interactivity.
Alternatives Considered
You might ask, “Why not stick with Vanilla JS or a lightweight library like Alpine.js?”
For straightforward interactions, those are valid and effective options. However, when your requirements include complex state management, component lifecycle methods, or tapping into the broader React ecosystem, React becomes the more advantageous choice.
Considerations
Performance: React adds weight to your JavaScript bundle. Use code-splitting with tools like Vite to load only what’s needed. For a lighter weight alternatives, you could consider libraries like Preact.
SEO: For critical content, ensure you include server-rendered HTML for React to hydrate. Use Razor to output static fallback content or consider SSR for key components.
Passing CMS Content: For demo purposes we set the JSON data in Razor. Try and do this in your Controller and for complex scenarios, you could even fetch data asynchronously via the Content Delivery API.
Error Handling: Build in graceful fallbacks for missing components or invalid data. Log errors clearly to assist debugging.
Accessibility: Pay attention to ARIA roles and keyboard navigation in your React components. Tools like Axe or Lighthouse can catch common issues.
Team Workflow: Define clear boundaries between your React and Umbraco teams to avoid conflicts. Shared conventions help when scaling.
Wrapping Up 🎁
This Islands Architecture-inspired approach offers the flexibility to integrate React-powered widgets or components where needed, while leaving the rest of your Umbraco site intact.
Although we’ve highlighted React here due to its popularity, this concept can just as easily be applied to other frameworks like Vue, Qwik, Solid, or any tool that fits your project’s requirements. The principles remain the same.
Hopefully, this serves as a useful example and sparks some ideas about how you might leverage this approach in your own projects. If you have suggestions for improving it or even better methods to share, I’d love to hear from you.