A Content Management System (CMS) is one of the very first tech investments a lot of companies make, and no matter how large the business is or what kind of CMS they choose, product & marketing teams need an effortless and safe way to author new components with their ever evolving ideas in order to serve their customers better and deliver more value.
The Advantages of Client Side Widgets
Building a client side widgets and deploying it as a static file make a lot of sense in some use cases that look similar to those:
Rich UI Interactions
If a widget is big on fetching data from external or 3rd party APIs and has a very dynamic interface and possibly client side data filters, dropdowns, sliders etc.. utilising a modern UI library will keep you highly productive by providing a functional approach to build and reuse elements, also by giving you a bunch of compatible off the shelf open source modules to import and use or just get some inspiration from.
Client side widgets can be served through your CDN and can be reused not only across the CMS pages, but also across the business’ different websites and web apps.
CMS pages are rendered on the server and that gives us a quick fast first paint with ready content, loading client side rendered widgets asynchronously that will progressively enhance the page without being a blocker.
Fast Independent Releases
Releasing a feature or a fix to my employer’s CMS for example takes a lot of time as there are release slots you have to check and book one a few days in advance, while a client side widget deployed as a static file and served over the CDN has helped us deploy as much and as often as we wanted.
Build in Isolation and Leverage Modern Tools
Some enterprise CMS are a few years old, and it’s very common to see a lot of JS files, grunt tasks and the answer to what’s your existing front-end architecture question is “No architecture really, just jQuery and stuff”. Building in isolation with all what modern tools has to offer such as hot module reloading, es harmony modules tree shaking is a win for both of the developers and the project.
Utilise a Shared Component Library
There are few popular Open Source frameworks like Storybook and Styleguidist for building, documenting and showcasing a set of elements and components that can be reused across all your widgets.
Progressive Widgets Architecture (PWA) with Preact
There are many reasons that make Preact an ideal library to build those widget, some of them are:
Tiny Library Size (Portability)
Preact’s tiny size will leave you a lot of room for your app’s code even when it’s bundled in your widget. It is only 3Kb, just around the size of the fetch polyfill.
Great Performance on Mobile
Easy to Pick Up
It doesn’t matter if a frontend developer is experienced in P\React or not, what makes picking up Preact fairly easy is the identical ES6 API to React’s API and lifecycle methods (up until latest stable) and there are some excellent blog posts, examples and tutorials developers can learn from.
Preact ecosystem has a lot of packages and modules to support a lot of common needs, it is also worth mentioning that you can utilise all react ecosystem modules and they will be fully compatible with your preact code via preact-compat. (PS: preact-compat will add 4.8 KBs).
Rendering in the CMS Page DOM
Looking at this simple widget as an example, you can easily tell we need to give ourself a better API and more flexibility:
And Then There Was Preact-habitat
Preact-habitat is just an extension to Preact’s render function where I decided to provide more API. The concept of Preact habitat is pretty straight forward and can be summarised as
- Scan the DOM when the widget’s script is loaded.
- Re-scan again when DOM is fully loaded.
- Allow host CMS page to pass props via HTML.
- Stay < 1KB.
Preact-habitat Implementation Details
Walking through the implementation details of preact-habitat, here’s a code snippet for our simple HelloWidget component with an option I have exposed inline the widget and mount it inside the script tag’s direct parent
Now that we have option inline set to true, to integrate this widget in your host CMS all we need to do is… use the script!
There are few other API options like like `clean:true` in case there was a default loading animation provided by the host CMS and you want to clear it before the widgets mounts:
Passing Props, Preact.h Trick & Render Function
The host CMS page developers will certainly love to be able to pass down some custom data that are relevant to that specific page, in preact-habitat all you need to do is add a script tag of type
text/props with a valid JSON in it, just like the following:
The first step of implementing this feature from scratch is getting the
text/props script’s content and parse the JSON to JS object
What makes passing the JS Object pretty easy is Preact’s pragma
h function that accepts 2 parameters, first one is the function or ES6 Class component we want to render and the 2nd one is you might have guessed, the props! (You can learn more about Preact’s h function here)
and the previous snippet is what I have utilised in preact-habitat as the following:
Rewriting is expensive and progressive enhancements are a good way to go around improving both of the user and developer experience, there are few ideas I look forward to bringing later this year like rendering in the Preact widgets in a shadow root which offers a pretty neat CSS encapsulation, but since Shadow DOM haven’t landed in all major browser and the polyfills are a little expensive I chose to wait a little longer, another thing you might want to look at if you are going to use Preact widgets extensively in your project is dynamic widget imports via webpack contextual require, this is a very neat trick Jason Miller has introduced to me once and I believe it is quite useful.
Some resources I recommend looking at:
- Preact Shadow Root: https://github.com/developit/preact-shadow-root
- Preact with React Styleguidist: https://github.com/styleguidist/react-styleguidist/tree/master/examples/preact
- Preact CLI and Preact CLI’s widgets template
Thank you for reading 💜.