In this article, we’ll look at a simple way to build your own content management system that makes editing and updating your website easy.
We created this article in partnership with Froala. Thank you for supporting the partners who make SitePoint possible.
What CMSs Are
Content Management Systems (CMSs) are powerful and popular tools for making platforms that are heavily dependent on content. They provide easy ways for users to create, publish, view, and edit content.
Also, they often require little to no coding experience. This makes them a good choice for bloggers, businesses, developers, or anyone who wants to build a website with less effort.
CMSs are used for different purposes across different fields. For example, they could be used to power blogs, company or freelance websites, educational websites, or even ecommerce platforms. Because of this, using a CMS remains a popular and relevant option, securing its place in web development for years to come.
There are plenty of available CMSs in the market. Some are open source, while others can be used for a price. Both open-source and paid CMSs are viable choices and have their advantages. There might also be instances where you want to create your own CMS.
You could be a developer seeking a challenge, a student tasked with creating a CMS, or a business that aspires to be a big name in the CMS industry. If you want to create a content management system, then this article is for you.
Preparing to Build Your Own CMS
We’ll be making a basic (i.e., usable enough) CMS using the following tools:
- HTML/CSS/JavaScript/jQuery for the frontend
- Bootstrap for responsiveness and easy design
- PHP for the backend
- MS SQL Server (or equivalent) for storing data
- SSMS (SQL Server Management Studio or equivalent) for creating the database and tables
- Froala, a WYSIWYG (what you see is what you get) HTML editor for content editing
While the CMS we’ll be making is basic, it will also be pretty solid. This is because Froala, the WYSIWYG editor we’ll be using, has most of the features you want in a CMS editor. This article should put you on the right track, whatever the end goal for your CMS is.
Also, take note that the DBMS (Database Management System) I’ll use is Microsoft SQL Server, because it’s what I’m most familiar with. You can easily swap it out for your preferred DBMS, such as MySQL or MariaDB. As we’re not doing anything particularly complicated with the database, DBMS choice doesn’t matter much here.
Before we proceed, let’s assume you already have PHP, the DBMS of your choice, and Froala installed on your computer. If you haven’t already, you can install them by visiting their respective websites and following the instructions.
What’s In a CMS?
Usually, in content management systems, there are administrators and end users. Admins manage the website’s pages, components, and content. They maintain the site, ensuring every feature works, and creating improvements where needed.
End users, on the other hand, interact with the pages and components of the CMS-powered website to create, edit, and consume content. Both users usually interact with a WYSIWYG editor within the CMS for content creation and editing.
For demonstration purposes, and to keep things simple, we’ll be implementing a single-page CMS for admins only. In a production CMS, we would need to consider many other factors that are beyond the scope of this tutorial. These include user management and access rights, security (input sanitization, parameterized queries, etc.), performance, and more.
In our simple CMS, admins should be able to carry out the following tasks:
- interact with a toolbar to create page components such as headings, text, links, and a Froala editor instance
- interact with a Froala instance to post content that will be displayed
- view posted content
- enter view mode, which hides the toolbar
Admins can also usually edit and delete page components, but let’s stick to creating and deleting components. With these functionalities in mind, let’s begin our process by creating a database schema.
Setting Up the Database
Since we have simple requirements, we also have a simpler database structure. For our sample CMS, we’ll be using only two unrelated tables:
- post
post_id
: int, identity(1,1), not null, primary keypost_content
: varchar(max), not nullposted_on
: datetime2(7), not null
- component
component_id
: int, identity(1,1), not null, primary keycomponent_type
: char(8), not nullcomponent_content
: varchar(255), nullablecreated_on
: datetime2(7), not null
The first table, post
, will store content from an editor component, while the component
table will store page elements generated using the toolbar.
Note that URLs have a max length of 2048 characters in most browsers, but in our database, we’ll set it to 255 characters only. Furthermore, you might also want to change the ID columns of both tables to randomized strings for your application.
Creating the Interface
We’ll include the CDN links for Bootstrap, Bootstrap Icons, and Froala’s CSS and JS files. We’ll also have our custom CSS and JS files:
<!--Include Bootstrap CSS-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
<!--Include Bootstrap Icons-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
<!--Include Froala Editor CSS-->
<link href="css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
<!--Include Custom CSS-->
<link href="css/index.css" rel="stylesheet" type="text/css" />
<!--Include Popper JS-->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.5/dist/umd/popper.min.js" integrity="sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk" crossorigin="anonymous"></script>
<!--Include Bootstrap JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.min.js" integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK" crossorigin="anonymous"></script>
<!--Include jQuery-->
<script src="https://code.jquery.com/jquery-3.6.1.js" integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=" crossorigin="anonymous"></script>
<!--Include Froala JS-->
<script type="text/javascript" src="js/froala_editor.pkgd.min.js"></script>
<!--Include Custom JS-->
<script type="text/javascript" src="js/index.js"></script>
The following components we’ll work on are the navbar and the toolbar. The navbar contains the site’s title or brand and an icon button for toggling the toolbar’s visibility (think of it as a “toggle admin view” button).
On the other hand, the toolbar contains four buttons corresponding to the four components that can be programmatically added to the page:
<div class="row">
<nav class="navbar navbar-expand-xl">
<div class="container-fluid">
<a class="navbar-brand text-white" href="#">Froala CMS</a>
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="bi bi-eye text-white" href="#" onclick="toggleToolbar()"></a>
</li>
</ul>
</div>
</nav>
</div>
The code above creates the navbar and components. The background color of the navbar is #0098F7
, giving it a blue hue. The toggleToolbar()
onclick event is responsible for hiding the toolbar elements. Next, write the code for the toolbar:
<div class="row mt-5 px-xl-5 mx-xl-5" id="toolbar">
<p class="lead ms-xl-5">
This is your CMS toolbar. You can add new components to your page by clicking any of the buttons below. To toggle the visibility of the toolbar, click the eye (<i class="bi bi-eye"></i>) icon on the navbar.
</p>
<div class="col-xxl-3 col-md-4">
<div class="m-xl-5 shadow p-xl-5 rounded border border-0 toolbarBox" data-bs-toggle="modal" data-bs-target="#headingModal">
<div class="row text-center display-4">
<i class="bi bi-type-h1"></i>
</div>
<div class="row text-center h3">
<label>Heading</label>
</div>
</div>
</div>
<div class="col-xxl-3 col-md-4">
<div class="m-xl-5 shadow p-xl-5 rounded border border-0 toolbarBox" data-bs-toggle="modal" data-bs-target="#textModal">
<div class="row text-center display-4">
<i class="bi bi-fonts"></i>
</div>
<div class="row text-center h3">
<label>Text</label>
</div>
</div>
</div>
<div class="col-xxl-3 col-md-4">
<div class="m-xl-5 shadow p-xl-5 rounded border border-0 toolbarBox" data-bs-toggle="modal" data-bs-target="#linkModal">
<div class="row text-center display-4">
<i class="bi bi-link-45deg"></i>
</div>
<div class="row text-center h3">
<label>Link</label>
</div>
</div>
</div>
<div class="col-xxl-3 col-md-4">
<div class="m-xl-5 shadow p-xl-5 rounded border border-0 toolbarBox" onclick="createComponent('editor')">
<div class="row text-center display-4">
<i class="bi bi-pencil-square"></i>
</div>
<div class="row text-center h3">
<label>Editor</label>
</div>
</div>
</div>
</div>
We use Bootstrap’s grid display (click here to learn more) and sizing options to make its button components for our toolbar. We’re also using Bootstrap icons alongside the button labels for better readability. When making your own CMS, there are plenty of ways you can represent your toolbar.
The usual options are the navbar, cards, or offcanvas elements. In this case, we represent them using card-like rectangles, each having a button-like behavior.
We’ll also add some events for our toolbar boxes. Note that we’ll be opening a modal whose code is shown below for each of the first three boxes (heading, text, and link).
Each contains an input field (two for links, one for text and one for the URL). Finally, we’ll call the createComponent
function for initializing our WYSIWYG HTML editor for CMS:
<div class="modal fade" id="headingModal" tabindex="-1" aria-labelledby="headingModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="headingModalLabel">Add a heading:</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<input type="text" class="form-control" id="headingContent">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" onclick="resetValue('heading')" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createComponent('heading')" data-bs-dismiss="modal">Save</button>
</div>
</div>
</div>
</div>
The above code shows one of the modals — specifically, the one used for adding a new heading to the page. This modal is shown when a user clicks the “Heading” button/card. It’s dismissed when the exit or cancel button is clicked, or a heading is added.
The other modals work similarly, but note that a proper link input should have two input fields for the text and URL (for now, we’ll use just one):
.toolbarBox:hover *, .toolbarBox:hover{
background-color:rgb(228, 228, 228);
cursor: pointer;
}
Finally, we’ll add the code snippet above to our index.css
file to change a toolbar box’s background color and mouse cursor on hover. Once finished, your user interface should appear as shown below.
Now that we’ve set up the interface, it’s time to add functionality with JavaScript.
Sending the Data
In our index.js
file, we’ll define five functions for the following features: creating components, toggling the toolbar visibility, resetting the input fields, loading posts, and loading components. Let’s see what each of them does.
-
createComponent(componentType)
We’ll use this function to send created components to the database. It takes
componentType
, which describes the type of component (link, heading, text, editor), as its parameter. First, we must determine the component type using aswitch-case
statement. For headings, texts, and links, we’ll just send their content along with their component type tophp/add.php
using an AJAX request. On the other hand, for editors, we’ll only need to send the component type. After the contents are successfully saved, we’ll call thegetComponents()
function to “refresh” the components displayed. -
toggleToolbar()
This function simply adds or removes the
d-flex
andd-none
classes from the toolbar as needed. -
resetValue(componentType)
A function that has the
componentType
parameter,resetValue
reinitializes the value of input fields by getting each element by ID and setting its value to an empty string. -
getComponents()
This function first makes an Ajax HTTP request to
php/load.php
to get the components’ data from the database. If successful, it dynamically generates HTML according to the components retrieved from the database (e.g., an<h1>
element for heading components). Afterwards, for every editor component, a Froala instance is initialized along with a save button. Lastly, aclick
event listener is added to the buttons for saving posts written within editors. On success, thegetPosts()
function is called to “refresh” the list of posts. -
getPosts()
The
getPosts()
function consists of an Ajax request that retrieves all posts from the database throughphp/loadPosts.php
. Similar togetComponents()
, this function dynamically generates HTML for displaying the posts.
On document load, both the getComponents()
and getPosts()
functions are called. Now that you have the necessary JS code for sending data to the server, all that’s left is to process it in the backend.
Preparing the Backend
As you may have noticed earlier, we have four PHP files — two for adding components and posts, and two for loading them. They consist of similar code, starting from connecting to the database with sqlsrv_connect
, getting the $_POST
variables (if any), and defining and running the query. Listed below are the queries involved in these files:
- load.php:
$query = "SELECT component_id, component_type, component_content FROM component ORDER BY created_on ";
- loadPosts.php:
$query = "SELECT post_id, post_content, posted_on FROM post ORDER BY posted_on ";
- add.php:
$query = "INSERT INTO component (component_type, component_content, created_on) VALUES (?, ?, GETDATE())";
- addPost.php:
$query = "INSERT INTO post (post_content, posted_on) VALUES (?, GETDATE())";
Once you set these PHP files up, you should be able to perform all the basic functions of a CMS with the editing power of a WYSIWYG editor. When making your own CMS, remember to sanitize input and parameterize the queries, among other security measures, to keep your users and applications safe from certain vulnerabilities.
Curious to see our base CMS in action? Let’s test it!
First, let’s create some page components using the toolbar we built.
As you can see, we can now easily create the components, which are saved into our component database table and displayed back to the interface using PHP. If we check our table using SSMS, we should see the data that’s shown below.
Now that we’ve created one of each component, including an editor instance, let’s try adding a post to our page.
In the video above, we can see some features that a powerful WYSIWYG editor can offer. These include font formatting, copy-pasting while preserving format, and image uploads. It also shows the number of characters at the bottom right corner for the character count-conscious writer.
After saving the content, we see it displayed just below the editor. You might want to add more padding and design or change the display completely for your posts, but this will do for now.
Froala provides over a hundred features that can enrich content editing and user experience. They’re also well placed to prevent overwhelming users. The editor is customizable, so you can include only the features you want your users to use.
Having saved the content of our post, we can now check the database table.
The image above shows the content of the editor we created, along with its ID and date of posting. You can also see that the HTML code on the post_content
column is preserved.
For larger CMS applications, you could store additional post information, including user details, comments, page interactions, and statistics, among other things. Of course, you’d have to make other related tables for this.
Finally, let’s see how end users view the page using our toolbar toggle button.
And there you have it — a CMS that you can build on; a rock-solid foundation for complicated CMS projects in the future.
What’s Next?
Now that you have the necessary tools and knowledge to build a simple but solid CMS, it’s time to determine your next steps. You can make plenty of improvements in transitioning this CMS into a full-blown one.
One improvement is the interface and the addition of new features. These include user access rights, comment sections, search engines, additional tools, page components, and themes, to name a few.
Another obvious improvement you can make is security. You can protect your CMS, server, data, users, and your business with it. We recommend checking out this link for a guide from OWASP (Open Web Application Security Project) that explains the best security practices.
You might also want to learn more about the WYSIWYG editor of your choice. For instance, you could check whether it has a comprehensive and easy-to-understand documentation page (click here to see an example).
Another way to familiarize yourself with an editor is to use its demos and free trial. And when you see the benefits it can bring, you can commit at your convenience. These editors save time, and you’ll need those extra months (or even years) for creating and polishing the other features of your CMS.
Lastly, you would need to read more about CMS best practices. They change from time to time, so being updated is always better.
We hope you enjoyed reading this guide and that your goal of creating a CMS now seems easier to attain.