
Your Design System in Jahia
Gautier Ben Aïm
A design system is a set of components that implement a visual language, and the guidelines to use them. In practice it usually consists of visual assets (Figma, Sketch, etc.), a library of components (React, HTML/CSS, etc.), and documentation. It serves as a single source of truth to enforce brand consistency across multiple products, from the CMS to business applications, even across teams and technologies.
Building a design system is an investment that pays off in the long run when several technologically different projects must look and feel the same to end users. Large organizations usually run a mix of portals, internal tools, CMS websites, mobile apps, and more. Your Jahia websites are part of that ecosystem, and a design system makes navigating between Jahia and other products feel seamless to end users. We told that story from the inside in The Journey of a Design System.
If you remember only one thing from this article, it’s this: the design system team and the CMS implementation team must coordinate often for the design system to succeed.
A design system is not a project to be built in isolation. It must be built in close collaboration with all its end users: integrators (the team building the website on top of the CMS) and editors (the people who will assemble pages out of these components day in, day out). It’s usually not practical to have editors in the loop, so integrators have to advocate for their technical needs and the editors’ functional needs. It’s uncommon for a design system to be built with CMS considerations like drag-and-drop editing or layout and sectioning components in mind, and integrators are the best people to anticipate authoring experience requirements.
Failure to coordinate will usually surface late in the project, when the design system is built, the integration is mostly done, and the editors start assessing the result.
The goal of this article is to help you avoid that situation. Integrating a design system into a CMS raises specific questions: here they all are, at every stage of the project, with the answers anticipated. We also covered the topic on video, in the replay Integrate your design system into Jahia.
HTML/CSS or React?
Jahia supports both HTML/CSS and React design systems: the choice is yours. Both have their advantages and disadvantages, so in case you haven’t made up your mind, let’s compare your options.
| HTML/CSS | React | |
|---|---|---|
| Pros |
|
|
| Cons |
|
|
If all consuming projects support React, we recommend going with a React design system. If you have to support technologies other than React, an HTML/CSS design system is the only option.
Both can be used in Jahia, but the rendering engine matters:
- JavaScript Modules are compatible with HTML/CSS and React design systems.
- JSP is only compatible with HTML/CSS design systems.
Jahia is the only traditional (non-headless) Java CMS that renders React components on the server. Your React design system runs inside the CMS rendering engine, not in a separate frontend application. We covered the mechanism in Under the Hood: Hydrating React Components in Java, and compared the approaches in our headless Java CMS guide.
Requirements to Share With the Design System Team
Three decisions have to be made with the design system team: which components it provides, how their CSS is produced, and whether organisms are exposed through composition or configuration. These three conversations are worth having early: they are cheap before the design system is built, and expensive afterwards.
What Components Should the Design System Provide?
Organisms, plus layout and sectioning components. That is the short answer, and it reads better with the vocabulary of the Atomic Design methodology, which distinguishes three levels of components:
- Atoms are the smallest building blocks of a design system: a tag, a button, an input.
- Molecules compose atoms into a single functional unit: a list of tags, a search input.
- Organisms compose molecules into an interface that solves a business problem: a blog post card, a footer, a page header.
Integrators will always end up creating organisms to build useful interfaces. If you want to enforce consistency and reduce duplication, provide and document organisms in your design system.
It’s also common for design system teams to overlook layout and sectioning components. They are required to build web pages: if the design system team does not provide them, integrators will have to build them themselves, which might lead to inconsistencies.
How to Style Components?
Two options, and only one of them works in every case: the design system compiles its CSS at build time and ships a file ready to import, or it lets every consumer compile it. The first one always works. This is the most technical concern in this article, and it is best addressed at the very beginning of the design system implementation.
React is intentionally unopinionated about CSS, so the design system has to make a choice. Here is what each option means for integrators:
-
Produce CSS at build time. The design system compiles all styles into a single
.cssfile from its own component code, and ships it. Consumers import that file and are done, no configuration:// Only needed once in the project, e.g. in the root layout import '@my/design-system/dist.css';The upside is bigger than the convenience: the styling technology becomes an implementation detail. The design system can migrate from Sass to CSS modules to anything else, and no consumer notices.
-
Produce CSS during consumption. Tailwind, CSS-in-JS, and similar approaches need to run inside the consumer’s build. The design system’s technology choices then become the consumer’s constraints: if the components are styled with Tailwind, every consumer needs a working Tailwind setup. This is also where it can break in Jahia: an approach that requires injecting styles into the server runtime is not compatible with JavaScript Modules.
Build-time CSS is always a valid option. Consumption-time CSS works when the same people write the design system and do the integration, as we do on Luxe, our demo website: its design system and its template set live in the same repository, so its components ship raw CSS modules and the template set’s own Vite build compiles them.
The two approaches, side by side:
| Build-time CSS | Consumption-time CSS | |
|---|---|---|
| Who compiles the styles | The design system | Every consumer |
| Consumer setup | Import one file | Replicate the design system’s toolchain |
| Compatible with JavaScript Modules | Yes | Only if nothing injects into the server runtime |
| Best when | The design system ships to other teams | The same people write the design system and do the integration |
Composition or Configuration?
Composition, in almost every case: it lets the integration team adapt organisms to what editors actually need, without going back to the design system team. This is the most consequential decision of the lot and, because it is not deemed "technical", it is often overlooked. Take a blog post, defined in Jahia as a content type:
[example:blogPost] > jnt:content, mix:title
- body (string, richtext) i18n
- tags (string) multiple i18n
- publishedAt (date)
A configured organism exposes props and populates itself from them:
<BlogPost
title={title}
body={body}
publishedAt={publishedAt}
tags={[...]}
/>
A composed organism exposes a single entrypoint, the children prop, and trusts the developer to use it correctly:
<BlogPost>
<h2>{title}</h2>
<div>{body}</div>
<Date ... />
<TagList ... />
</BlogPost>
The difference looks stylistic, but it has real implications for who owns changes to the design system:
- With configuration, the design system team fully controls the appearance of everything rendered by the CMS. Integrators have to go back to that team, and advocate for their editors’ needs, every time reality doesn’t fit the props.
- With composition, the integration team can adapt the organisms to what editors actually need.
In practice, here is a footer with two columns, configured:
[example:footer] > jnt:content
+ leftCol (example:footerColumn)
+ rightCol (example:footerColumn)
<Footer
leftCol={<RenderChild name="leftCol" />}
rightCol={<RenderChild name="rightCol" />}
/>
It works, and editors can use it.
Now the same footer, composed:
[example:footer] > jnt:content orderable
+ * (example:footerColumn)
<Footer>
<RenderChildren />
</Footer>
It works better: editors can reorder the columns by dragging them, and a third column costs nothing on the CMS: no new property, no CND update.
Configurable components are hard to maintain and evolve over time. On the other hand, composition allows consumers to implement new behaviors (e.g. drag and drop in the editor) without changing the design system.
Two side notes, which are in fact two sides of the same coin:
- The configuration pattern is easily built on top of the composition pattern, so a composed design system loses nothing. The reverse is not true.
- HTML/CSS design systems are always based on composition: that’s all markup can do.
None of this means the design system should be a free-for-all. A design system that is too strict makes editing interfaces impossible; one that is too loose makes them messy. Integrators are usually the best people to find the balance because they are the only ones who see both the components and the editors.
Using a Design System in a Java CMS: The Jahia Case
The integration itself is short: a few imports in your template set, and the design system is usable from your views. We strive to offer an optimal Developer Experience in Jahia, and we walked through the full mechanics in the webinar JavaScript Modules, JSX and React.
Consuming an HTML/CSS Design System
This is the route for design systems that ship CSS classes (e.g. Bootstrap or DSFR), and for JSP projects. Note that DSFR also has React bindings, so it can equally go the React route described just after.
First, link the CSS and JS resources in your template set. Vite’s explicit URL imports give you the asset URL, and AddResources puts the tag in the page:
import '@gouvfr/dsfr/dist/dsfr.min.css'; // yarn add @gouvfr/dsfr
import js from '@gouvfr/dsfr/dist/dsfr.module.min.js?url'; // ?url is a Vite feature
<AddResources
type="inline"
targetTag="body"
inlineResource={`<script type="module" src="${buildModuleFileUrl(js)}"></script>`}
/>
Then use the CSS classes in your views:
import { jahiaComponent } from '@jahia/javascript-modules-library';
jahiaComponent(
{
componentType: 'view',
nodeType: 'dsfr:callout',
},
({ title, body }) => (
<div className="fr-callout">
<h3 className="fr-callout__title">{title}</h3>
<p className="fr-callout__text">{body}</p>
</div>
),
);
That’s it! The same approach works with JSP, with a slightly different syntax for the AddResources tag.
Consuming a React Design System
This is harder to anticipate because it depends on the implementation details of the design system. It should boil down to two steps. First, install the design system with a package manager.
Then import the components and render them from a view:
// Footer/default.server.tsx
import { jahiaComponent, RenderChildren } from '@jahia/javascript-modules-library';
import { Footer } from '@my/design-system';
jahiaComponent({ componentType: 'view', nodeType: 'example:footer' }, () => (
<Footer>
<RenderChildren />
</Footer>
));
If the design system ships a single CSS file, import it once in the project, in the root layout for instance. Otherwise, follow the design system’s own instructions: it may work out of the box thanks to Vite (CSS modules, @emotion/styled v10+), or need a Vite plugin (Tailwind).
If the design system does not ship usage instructions, ask the design system team: it’s their responsibility to provide them.
Interactive Components
React is the preferred choice for interactive components. But Jahia uses React as a template engine for server-side rendering, which means that by default Jahia ships 0 bytes of JavaScript to the browser. An interactive component from your design system therefore needs an explicit bridge: the <Island> component, which we covered in Leveraging the Island Architecture in Jahia CMS.
Wrap the design system component in a client component (file ending with .client.tsx):
// Accordion.client.tsx
import { Accordion } from '@my/design-system';
export default function AccordionClient({ title, body }) {
return <Accordion title={title}>{body}</Accordion>;
}
Then hydrate it from the server view (file ending with .server.tsx):
// default.server.tsx
import { Island, jahiaComponent } from '@jahia/javascript-modules-library';
import AccordionClient from './Accordion.client.tsx';
jahiaComponent(
{
componentType: 'view',
nodeType: 'example:accordion',
},
({ title, body }) => <Island component={AccordionClient} props={{ title, body }} />,
);
The accordion is rendered on the server, then hydrated in the browser. The rest of the page stays static HTML.
Exposing a Design System to Editors
Expose organisms, never atoms, and molecules only in their context. This is the last question, and the one that decides whether editors enjoy the result: which components become droppable in Jahia?
A design system is built for developers, not for editors. Exposing all of it, i.e. making every component droppable and calling it a day, drowns editors in choices they cannot use. Our rule of thumb, by Atomic Design level:
- Never expose atoms. They do not make sense in isolation and editors will not know how to use them.
- Expose molecules only in the context where they make sense. A
<FooterColumn>can be created in the page footer, and nowhere else. - Expose organisms that editors can actually place. They solve a business problem, which is exactly what an editor is trying to do, but some only make sense in one context, and some are generated rather than authored.
In practice:
| Component | Category | Expose? | Why |
|---|---|---|---|
<Button> |
Atom | Don’t | Meaningless on its own. |
<FooterColumn> |
Molecule | In context only | Creatable in the page footer, nowhere else. |
<TagList> |
Molecule | Probably not | Populated automatically from out-of-context editing. |
<PageSection> |
Organism | In context only | Expose at the root of pages, for visual structure. |
<BlogPostCard> |
Organism | Do | It solves a problem an editor actually has. |
<PageHeader> |
Organism | Don’t | Generated from the page title and the site tree. |
Not all organisms are useful to editors, e.g. a page header might be fully generated from the page title and the site tree, so editors should not be able to create it.
And if the design system has no organisms at all? Build them in Jahia, and expose only those. They would have been better in the design system, but at least editors will have a usable interface. That is the whole subject of How Integrators Can Design the Ultimate Content Author Experience in a CMS.
Try It Out!
Jahia CMS is open-source and free to try. Creating a new module is a single command:
npm init @jahia/module@latest
The CLI will guide you through starting a Jahia instance locally and uploading your module to it. Our introduction for frontend developers takes it from there, and the Jahia developer hub gathers the rest.
Conclusion
Bringing a design system into a CMS is mostly a coordination problem, and the technical part is the easy half. The design system team and the CMS implementation team must coordinate often for the design system to succeed. The design system is a product for developers, and integrators are the ones who know what editors need. Get those two teams in the same meeting, early.
If you want to talk any of this through on your own project, book a free 30-minute session with me.
FAQ
Can you use a Tailwind design system in a CMS?
Yes, provided the CMS can run the Tailwind toolchain inside its own build. Tailwind produces CSS at consumption time, so every project consuming the design system needs a working Tailwind setup. In Jahia this works with a Vite plugin, unless the approach requires injecting styles into the server runtime, which JavaScript Modules do not support.
Should a CMS design system be React or HTML/CSS?
React if every consuming project supports React: components are reused as they are, with type-safety and autocomplete. HTML/CSS as soon as JSP, PHP, Angular or Vue also have to be served, because it is then the only compatible option. In Jahia, JavaScript Modules accept both, while JSP only accepts HTML/CSS design systems.
Which design system components should be exposed to CMS editors?
The organisms that solve a real business problem, such as a blog post card or a page section. Never atoms: on its own, a button or a tag means nothing to an editor. Molecules only in their context, for example a footer column creatable in the footer and nowhere else. Automatically generated components are not exposed.
Composition or configuration: which pattern should an organism use?
Composition. A composed organism exposes a single entrypoint, the children prop, which lets the integration team adapt rendering to what editors actually need without going back to the design system team. The configuration pattern is easily rebuilt on top of composition, while the reverse is not true. HTML/CSS design systems are composed anyway.
Is a design system worth it for a single website?
The investment mostly pays off when several technologically different projects must look and feel the same: portals, internal tools, CMS websites, mobile apps. On a single site with a single team, the cost of building and documenting it often exceeds the gain. The deciding factor is the number of consuming projects, not the size of the site.
Discover






