I recently worked a bit in the main product of Alokai -> The Unified Storefront, and my work was primarly focused on improving performance of the project. I will share about my interesting findings in the next blog post :)
One of my findings was related to components that are usually paired with Content Management Systems (CMS) to display dynamic content on the website. In order to optimize these components, I needed to develop a new functionality in both Vue and React frameworks (as we support both in the product).
Implementing these improvements in Vue (Nuxt) was relatively easy because I work with it everyday, but doing so in React (Next) was a bit of a challenge.
And considering that during my development, I haven't found a simple solution to solve this problem (especially in React) and my solution was a mix of few answers, I decided to write this article to make it easier for you :)
Enjoy!
🤔 What is the use case here?
In general, the use case is to have a component that will be able to automatically style the content that will be received from CMS. The issue here is that we want to accept pure HTML as a content.
So, we want to be able to pass following HTML in CMS:
<h1>Hello World</h1>
And by using our component like so:
<Editorial content="htmlContentFromCMS" />
Automatically apply styling so that the final HTML would look like this:
<h1 class="text-2xl">Hello World</h1>
Sounds interesting right? Let's take a look how we can achieve that in both Vue and React frameworks :)
🟢 Dynamic HTML styling in Vue
In Vue/Nuxt, this concept can be implemented relatively simple.
In the template section we need a simple div
tag where we will pass raw HTML that we will get from the CMS:
<template>
<div v-html="content" />
</template>
In the script section, we only want to define props of our component to allow it to accept content
prop that will contain the HTML content from CMS.
<script lang="ts" setup>
defineProps({
content: {
type: String,
default: '',
}
});
</script>
And in the style section, we want to create rules for the HTML tags and the styling that we will create in our design system:
<style scoped>
:deep(h1) {
@apply text-2xl;
}
</style>
Notice that the style section has an attribute scoped
which means that this styling will be only applied as a part of this component but as this HTML from CMS is different content that statically in the component, we need to pass a :deep
helper before the tag name (h1
). Thanks to this, we will style the content receive from CMS but other global tags h1 won't be affected.
🔵 Dynamic HTML styling in React
In React/Next, this is a bit more complex. First of all, in React components, we don't have that simple way of creating styles per tag/class/id without using an external library like styled components.
The solution to this would be to use the css modules that you can read more here. But this solution also comes with some problems because with it, we cannot style the tags properly receiving following error:
Selector "h1" is not pure (pure selectors must contain at least one local class or id)
You can read more about this issue here and here
Thanks to all these resources, I have found a solution that is working in my case.
First, let's create a editorial.module.css
file:
// editorial.module.css
.editorial h1 {
@apply text-2xl;
}
Notice the .editorial
class. This is a way of bypassing the issue with pure selectors. Otherwise the style would not be applied.
Next, in our component:
import styles from './editorial.module.css';
type EditorialProps = {
content?: string;
};
export default function Editorial({ content }: EditorialProps) {
return (
<div dangerouslySetInnerHTML={{ __html: content }} className={styles.editorial} />
);
}
Let's stop for a second to explain each step here:
- We import the styles from the module css file.
- We define a type for the props that this component will accept
- We create a new component and export it that accepts the content as a prop
- This component will return inner HTML with the content from CMS and as a class name, we will pass the styles from module css file
And that's how it works!
📖 Learn more
If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:
It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉
✅ Summary
Well done! You have just learned how to create a component in Vue and React that will be able to display content in a form of a pure HTML fetched from CMS.
Take care and see you next time!
And happy coding as always 🖥️
Top comments (2)
I've been struggling with this issue in my Vue app while trying to style the dynamic html created via Tiptap. I initially used since v-html doesn't work with <style scoped> by default. The solution worked like charm on localhost, but in production it gives a mimetype problem. I figured the only way out was to ditch the whole idea of using <style module> and come back to scoped. This article just saved my day. Thanks a lot!</p>
Happy that it helped you! :)