Redstone Content Solutions is a certified dotCMS partner with deep domain expertise in Enterprise Content Management Systems. Having worked with and evaluated various platforms for over 20+ years, their expertise spans every phase of the content lifecycle, from planning to optimization, and helps dotCMS clients succeed.
Introduction
About 1 year after I started working with another content management system, I was called into a meeting with my boss and my main client at the time. The client had a massive amount of content, thousands of pages, at least 30 content types, integrations with two different APIs, and more news articles than I knew how to count. I was told that my job was to get all that into the CMS. I was terrified. I had worked in the system for a bit by that point and had more than a couple certifications, but the scope of this project dwarfed anything else I had done. That was more than 5 years ago now, and I did end up getting all that content into the CMS. When I started working with dotCMS, I was excited because over those 5+ years, I learned all kinds of things. I learned about content management systems and development, what things to avoid, and how to do them better. Now I could use all of that knowledge to make my life and the lives of my clients easier, and I think I can sit down and share that knowledge with other people. Hopefully, they don't run into the same frustrations I did. This post will go through some of the most important lessons I have learned and how I have used them to make the transition to dotCMS as smooth as possible.
Lesson 1 - Start with Content Audit, Not Technology
The problem: A problem I ran into with my previous CMS when I first started was jumping straight into the technical implementation before I fully understood the content model and landscape of the system. In many CMS systems, the initial implementation of content is very important. Once a content model is established, it can be very hard to change it, especially if there is already a lot of content in the system.
The solution: I learned quickly to take the time to understand the content model and landscape of the system before starting the implementation. So when approaching dotCMS, I blocked out time specifically to model and map the content we needed to display into their counterparts in dotCMS. This helped a ton in the long run, but even then, changes were needed later as the scope changed or enhancements were requested. I had expected this to be quite a problem, but it ended up being a lot easier than I thought it would be. In dotCMS, the content model is very flexible in that even after content has been added to the system, we do not have to remove or delete created content. Instead, the fields could be changed, and the content would be updated to match the new model. This saved me about 8 hours of updating content and allowed me to focus on making the enhancements. It even allowed me to test out a couple of different ways to add the feature without worrying about breaking anything!
Lesson 2 - Build Reusable Components Once, Use Everywhere
The problem: In content management systems, it's common to have more than a few ways to create the same content. In a lot of CMS systems, it's common to have rigid archetypes of content that can be created to help divide this content based on the expected use. The problem I always had with this is that the intended use case of a piece of content can change or begin to expand over time. I can't count the number of times I implemented a component that was supposed to be static as a content layout, but was later asked to enhance it in a way that required me to basically create a whole new layout. One time, I remember I even ended up implementing so many content layouts for a piece of content that users had trouble finding the correct one in the settings dropdown!
The solution: Flexibility is key! As I went on, I began to break things into smaller and smaller pieces to make it easier for them to be reused in ways that hadn't been intended. This allowed me to create content layouts that were more flexible and allowed for more reuse. This approach is even easier in dotCMS thanks to my favorite motto in it: "Everything is content". In dotCMS, all things are content, meaning that when I created a component, I could use it in any way I needed to. I could create simple building block components that were highly configurable and then reuse them for entirely different purposes. In an example below, I show how I used a basic design of a carousel for two different purposes using JavaScript native web components and the dotCMS content model.
Real-World Example: In the example below, I created a basic web component for displaying a client's projects in a carousel. I truncated it quite a bit here because it's fairly complex, but the point I want to show should still hold. The component is a carousel that can display some content it receives and allow the user to cycle through it with arrows to either side.
class FeaturedContent extends HTMLElement {
connectedCallback() {
this.render();
}
render() {
this.shadowRoot.innerHTML = `
<style>
.content-container {
position: relative;
overflow: hidden;
}
.contents {
display: flex;
gap: var(--gap);
scroll-behavior: smooth;
overflow-x: scroll;
}
::slotted(.content-card) {
flex: 0 0 var(--card-basis);
scroll-snap-align: start;
}
</style>
<div class="contents-container">
<div class="content">
<slot></slot>
</div>
<button class="arrow arrow-left">Previous</button>
<button class="arrow arrow-right">Next</button>
</div>
`;
}
}
customElements.define("featured-content", FeaturedContent);
Now comes the fun part! In this first example, I use that new web component to display a bunch of project cards, allowing the user to leaf through them.
<featured-content header="Featured Projects">
#foreach($project in $dotContentMap.projects)
<a href="/projects/$project.urlTitle" class="project-card">
<base-card class="imagefocus-card">
<div slot="section">
<div class="imagefocus-wrapper">
<img src="${project.projectImage.resizeUri}400maxw/250maxh"
alt="$project.projectTitle">
<div class="overlay-content">
<h2 class="overlay-title">$project.projectTitle</h2>
#if($project.subtitle)
<p class="overlay-subtitle">$project.subtitle</p>
#end
</div>
</div>
</div>
</base-card>
</a>
#end
</featured-content>
Our client found that so useful that they decided that they wanted to do something like build a company timeline. Because I had already built the component to care little about the content it rotated through, it meant that I could use the same component for a different purpose!
<featured-content header="Company Timeline" modal-images>
#foreach($event in $dotContentMap.events)
<div class="event-card">
<base-card class="imagefocus-card">
<div slot="section">
<div class="imagefocus-wrapper">
<img src="${event.image.resizeUri}600maxw/400maxh"
alt="$event.title">
<div class="overlay-content">
<h2 class="overlay-title">$event.title</h2>
#if($event.subtitle)
<p class="overlay-subtitle">$event.subtitle</p>
#end
</div>
</div>
</div>
</base-card>
</div>
#end
</featured-content>This might seem like a small thing, but thanks to this, I saved probably 16 hours of planning and writing a new component to do the same thing as my featured projects component, with only superficial changes. In this case, the main difference was in the data displayed in the cards and the action they took when clicked.
Lesson 3 - Make Development and Maintenance Easy
The problem: Content models will shift and change over time, meaning that we need to be able to make rapid changes in a way that is easy to maintain and easy for developers to use. We are a long way from the days of uploading files to a remote server and testing, only to do it again and again as you find you've misplaced a comma or a colon (Wasn't that fun?). We need a way to rapidly iterate and develop.
The solution: dotCMS has several tools to enable this, but the most important one is dotCLI. dotCLI is a command-line interface that allows you to interact with dotCMS in a way that is easy to use and understand. It allows you to create, update, and delete content, as well as manage users and permissions. It also allows you to export and import content, which is invaluable when you need to move content from one environment to another (Especially when migrating between instances!). Most importantly, it allows for easy testing and deployment of changes to the system, including the ability to run a local instance of dotCMS to test changes before deploying them to any remote instance, whether it is a staging environment or a production environment. It even allows easy management of multiple remotes and environments at once through the use of API keys and configuration files. You can find more information about dotCLI and how to use it in the dotCMS documentation linked below. The dotCMS documentation covers this so thoroughly that I'll just point you there rather than duplicate it!
Lesson 4 - Phase the Migration
The problem: When starting a large migration from one platform to another, it's tempting to line up all of your content and then migrate all at once. With any migration, no matter how well planned, issues are expected (At least if you have my experience!). The more content that is being migrated, the more likely you are to encounter unforeseen issues or complications. Even with dotCMS's migration tools like batching import or the ability to create import scripts using the built-in APIs, it can still be tough to manage.
The solution: The best way to tackle this is to phase the migration planning to do so in pieces, starting with the easiest content that maps the most readily to the new system. This lets you learn the ins and outs of the process and gives you time to focus on planning for scale and complexity. You won't always be able to do this, but running the systems in parallel is a huge help. It makes for a smoother cutover and allows you to test the new system in a way that is more representative of the final environment.
Recommended Phases:
Content Audit
Simple content type creation and testing
Simple content migrations
Complex features and content types (search, integrations, etc.)
Parallel running and testing
Gradual cutover
Doing it this way allows you to transition to a new system in a way that is way less stressful and allows you to focus on the most important aspects of the migration (like making sure nothing is left behind!).
Lesson 5 - Media Management and Display
The problem: In any CMS, it is inevitable that you will build up a large collection of media files. This can be a pain to manage and display in a way that is consistent and easy to use. It can also lead to duplication of images to be used for different purposes, such as using the same image on a detail page as a thumbnail on a listing page. Since the areas will have widely different dimensions of the display area, you end up having to create multiple versions of the same image to be used in different contexts. This is a real pain and can lead to a lot of extra work.
The solution: dotCMS has a number of tools to help with this, but the most important one is the resize API. All image URLs generated by dotCMS can be resized using path parameters to the image URL. This makes it easy to resize your images on the fly and use them in different contexts. There are also parameters to control the quality and the format of the image. All of this can be done without any additional processing or storage of the images. This means no more duplicate images! Simply modify the URL to resize the image to the desired dimensions, and you are good to go! Below is an example of how to use the resize API from a recent project where there was content from an event that needed to be used in a listing and a detail page. Thanks to the resize API, we were able to use the same image for both without having to create multiple versions of the image.
// results in image with the default dimensions
<img src="${image.shortyUrl}" alt="$event.title">
// results in image with a max width of 600px and a max height of 400px
<img src="${image.shortyUrl}600maxw/400maxh" alt="$event.title">Pretty simple, right? For more information on the resize API, you can find it in the dotCMS documentation here: https://dev.dotcms.com/docs/image-resizing-and-processing
Common Pitfalls to Avoid
Here are a few common pitfalls to avoid when implementing dotCMS, along with some stories of how we handle them:
Pitfall 1: Underestimating Training Needs
My experience: In my first project, this was a killer. When doing the estimating, I had assumed that the contributors would pick up the system the same way that I did, through exploration and discovery. Because of that, I had planned for only three training sessions. By the end of the third one, I was still being asked about stuff we covered in the first... I ended up having to create a whole training video to cover the basics, and even built a small website to act as a wiki for the system. After that, I learned that training isn't just a one-time thing; it is an ongoing process. My favorite approach now is to teach a few high-level users so that they can train others.
Lesson: Schedule hands-on training sessions for content editors. Like all CMS systems, dotCMS has a learning curve, and it is important to give content editors the time and resources they need to learn the system. It is also important to give them the time to practice and become comfortable with the system.
Pitfall 2: Poor Stakeholder Communication
My experience: In one project, I was asked to add enhancements to a card component. I had never talked directly with the stakeholder, just had a document with the requirements. It all seemed pretty simple, so I got right into it and started cranking it out. I finished it up with time to spare and was feeling pretty proud of myself. The problem was... It was completely wrong. I had misunderstood the requirements and had no idea what the stakeholder was asking for. I ended up having to scrap the whole thing and start over.
Lesson: Communication is key in any project, but when performing a migration from one system to another, managing expectations on timelines is a must. This is even more important if migrating from a sunsetting system to a new system. Progress updates are always appreciated and can help keep stakeholders informed and engaged.
Pitfall 3: Strict Content Model
My experience: I had a project where I was building a content header. It was a simple enough component: an image, a title, and a subtitle. It worked great for a while, but then my client wanted to be able to add another component to the content header, a slider. This proved to be a real headache because when I had built the content model for the header, I had expected it would only ever be an image displayed inside of it. Since I had already built the component and there were already tons of headers built, I couldn't easily change the content model. I ended up having to create a script to update all of the existing headers just so that I could change the content model to be more generic. I now know from experience that it took me literal days more to write the scripts and update the headers than it would have been to just have the content model be more flexible from the start.
Lesson: Content models are important, and content should be structured in a way that is easy to understand and use, but it is just as important that the content model is flexible and allows for changes as the project progresses. Break things down into smaller pieces and allow for reuse and flexibility. That way, should changes be needed, the scope to do so is much smaller.
Conclusion
As I have worked through our own migrations to dotCMS, I have tried to implement all of these lessons, and because of them, the whole process has been easier. I've even picked up a few new skills along the way, and I'm excited to see what the future holds for dotCMS and the projects we will be working on. I am especially looking forward to the new Contributor UI overhaul and the new dotCLI features that are coming in the future. I hope the lessons I learned will be as useful for you as they have been for me and that they help you weather the only thing I have found to be a constant in web development: change.