Why Build a Blog with CodeIgniter 4, MySQL, and Bootstrap?
CodeIgniter 4 remains one of the most popular PHP frameworks in 2026, thanks to its lightweight footprint (no heavy dependencies), excellent documentation, and blazing-fast performance. Unlike heavier frameworks like Laravel, CodeIgniter gets you up and running quickly — making it the perfect choice for small-to-medium projects like personal blogs, company news sections, or learning demos.In this step-by-step tutorial, you will learn how to build a simple blog application from scratch using CodeIgniter 4 for the backend, MySQL for database storage, and Bootstrap 5 for responsive frontend styling. We will cover project setup, database migration, MVC architecture (Model, View, Controller), routing, and complete code examples.
Key benefits of using CodeIgniter 4:
- Built-in support for .env configuration for environment management
- Namespaced controllers, models, and views following PSR-4 standards
- Powerful CLI tools (
spark) for code generation and migrations - Easy integration with Bootstrap for responsive frontend design
- Built-in security features like CSRF protection and input escaping
What You Will Build:
By the end of this tutorial, you will have a working blog that can display a list of posts, show individual post details, and create new posts — all powered by CodeIgniter 4's MVC architecture.
Related Tutorials: If you are new to CodeIgniter 4, you may also want to read our guide on CodeIgniter 4 vs Laravel 12: Which is Better for Small Projects? and How to Boost CodeIgniter 4 Performance to understand why CI4 is an excellent choice for your next project.

Table Of Content
1 Prerequisites
- PHP >= 8.1 — CodeIgniter 4 requires PHP 8.1 or higher. Check your version with
php -v - Composer — PHP dependency manager. Install from getcomposer.org
- MySQL or MariaDB — A running database server to store blog posts
- Web server (Apache/Nginx) with the rewrite module enabled, or use CodeIgniter's built-in development server via
php spark serve - A code editor — VS Code, PhpStorm, or any editor of your choice
If you haven't installed CodeIgniter 4 before, don't worry — the next section covers the complete installation process.
2 Introduction
In this tutorial, we will guide you step-by-step through the process of creating a simple blog using CodeIgniter 4, MySQL, and Bootstrap 5. By the end of this article, you will have a fully functional blog that supports Create and Read operations, styled with a clean responsive layout using Bootstrap.
CodeIgniter 4 follows the MVC (Model-View-Controller) design pattern, which separates your application logic from the presentation layer. This makes your code more organized, testable, and maintainable.
Note: This tutorial focuses on the Create and Read parts of CRUD. For a complete CRUD implementation including Update and Delete with a REST API approach, check out our CodeIgniter 4 REST API CRUD Tutorial. To add user authentication, see our CodeIgniter 4 Authentication Tutorial.
3 Create / Install a Codeigniter 4 Project
3.1 Install Codeigniter 4 Project
Use the following command to install new Codeigniter Project.
composer create-project codeigniter4/appstarter ci4-blog-app
Then, navigate to your project directory:
cd ci4-blog-app
3.2 Configure Environment and MySql Database
# CI_ENVIRONMENT = production
CI_ENVIRONMENT = development
database.default.hostname = localhost
database.default.database = blog_db
database.default.username = root
database.default.password =
database.default.DBDriver = MySQLi
Create the database blog_db
4 Create the Posts Table Migration and Model
php spark make:migration CreatePostsTable
Edit app/Database/Migrations/YYYYMMDDHHMMSS_CreatePostsTable.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePostsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'slug' => [
'type' => 'VARCHAR',
'constraint' => '255',
'unique' => true,
],
'content' => [
'type' => 'TEXT',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('posts');
}
public function down()
{
$this->forge->dropTable('posts');
}
}
Run the migration:
php spark migrate
Create Model for the posts table:
php spark make:model Post
Edit app/Models/Post.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class Post extends Model
{
protected $table = 'posts';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $allowedFields = ['title', 'slug', 'content', 'created_at', 'updated_at'];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
public function getPosts($slug = false)
{
if ($slug === false) {
return $this->orderBy('created_at', 'DESC')->findAll();
}
return $this->where(['slug' => $slug])->first();
}
}
5 Create Controller
php spark make:controller Blog
Edit app/Controllers/Blog.php:
<?php
namespace App\Controllers;
use App\Models\Post;
use CodeIgniter\Exceptions\PageNotFoundException;
class Blog extends BaseController
{
protected $postModel;
public function __construct()
{
$this->postModel = new Post();
}
public function index()
{
$data = [
'title' => 'My Simple Blog',
'posts' => $this->postModel->getPosts()
];
return view('blog/index', $data);
}
public function view($slug)
{
$post = $this->postModel->getPosts($slug);
if (!$post) {
throw PageNotFoundException::forPageNotFound();
}
$data = [
'title' => $post['title'],
'post' => $post
];
return view('blog/view', $data);
}
// Simple create (for demo - add form validation in production)
public function create()
{
if ($this->request->getMethod() === 'post') {
helper('text');
$data = [
'title' => $this->request->getPost('title'),
'slug' => url_title($this->request->getPost('title'), '-', true),
'content' => $this->request->getPost('content')
];
$this->postModel->insert($data);
return redirect()->to('/blog')->with('success', 'Post created!');
}
return view('blog/create', ['title' => 'Create New Post']);
}
}
Important Notes:
- The
getPosts()method is called without arguments to fetch all posts, or with a slug to fetch a single post - We use
PageNotFoundExceptionto return a 404 error when a post is not found - The
url_title()helper automatically generates URL-friendly slugs from the post title - For production, always add input validation and security measures to protect against SQL injection and XSS attacks
6 Create Views with Bootstrap 5
layout.php (base template - app/Views/layout.php):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= esc($title ?? 'My Blog') ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">My Blog</a>
</div>
</nav>
<?= $this->renderSection('content') ?>
<footer class="bg-dark text-white text-center py-4 mt-5">
<p>© <?= date('Y') ?> My Simple Blog - Built with CodeIgniter 4</p>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Create folder app/Views/blog/index.php (List View - app/Views/blog/index.php):
<?= $this->extend('layout') ?>
<?= $this->section('content') ?>
<div class="container my-5">
<h1 class="mb-4"><?= esc($title) ?></h1>
<a href="/blog/create" class="btn btn-primary mb-4">Create New Post</a>
<?php if (empty($posts)): ?>
<p>No posts yet.</p>
<?php else: ?>
<?php foreach ($posts as $post): ?>
<div class="card mb-4">
<div class="card-body">
<h2 class="card-title"><a href="/blog/<?= esc($post['slug']) ?>"><?= esc($post['title']) ?></a></h2>
<p class="card-text"><?= substr(strip_tags($post['content']), 0, 150) ?>...</p>
<small class="text-muted">Posted on <?= date('M d, Y', strtotime($post['created_at'])) ?></small>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?= $this->endSection() ?>
view.php (single post - app/Views/blog/view.php):
<?= $this->extend('layout') ?>
<?= $this->section('content') ?>
<div class="container my-5">
<h1><?= esc($post['title']) ?></h1>
<small class="text-muted">Posted on <?= date('F d, Y', strtotime($post['created_at'])) ?></small>
<div class="mt-4">
<?= $post['content'] ?>
</div>
<a href="/blog" class="btn btn-secondary mt-4">Back to Blog</a>
</div>
<?= $this->endSection() ?>
create.php (Create post - app/Views/blog/create.php):
<?= $this->extend('layout') ?>
<?= $this->section('content') ?>
<div class="container my-5">
<h1>Create New Post</h1>
<form action="/blog/create" method="post">
<?= csrf_field() ?>
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" id="content" name="content" rows="10" required></textarea>
</div>
<button type="submit" class="btn btn-success">Publish Post</button>
</form>
</div>
<?= $this->endSection() ?>
7 Define a Route
$routes->get('/', 'Blog::index');
$routes->get('blog/create', 'Blog::create');
$routes->post('blog/create', 'Blog::create');
$routes->get('blog/(:segment)', 'Blog::view/$1');
Route Breakdown:
GET /— Displays the blog homepage listing all postsGET /blog/create— Shows the form to create a new postPOST /blog/create— Handles form submission and saves the postGET /blog/(:segment)— Displays a single post by its slug. The(:segment)placeholder matches any URL segment
Tip: Make sure the
blog/create route is defined before blog/(:segment) to prevent CodeIgniter from treating "create" as a slug. 8 Folder Structure
9 Run and Test
php spark serve
Visit http://localhost:8080 in your browser. You should see the blog homepage. Click "Create New Post" to add your first blog entry, then verify it appears on the homepage and that clicking the title opens the single post view.Troubleshooting Tips:
- If you see a database connection error, verify your
.envfile credentials and ensure MySQL is running - If you get a 404 error, make sure the migration was run successfully with
php spark migrate - Check the
writable/logs/directory for detailed error logs
Related CodeIgniter 4 Tutorials
- CodeIgniter 4 Authentication Tutorial — Build a complete login and registration system using CodeIgniter Shield
- Role-Based Login System in CodeIgniter 4 — Implement admin and user role separation for your blog
- CodeIgniter 4 REST API CRUD with Postman — Learn full Create, Read, Update, Delete operations via a REST API
- Secure Your CodeIgniter 4 App from SQL Injection — Essential security practices for production applications
- Boost CodeIgniter 4 Performance — Optimization tips for faster page loads and better user experience
- Redis Cache in CodeIgniter 4 — Speed up your blog with in-memory caching
- Multi-Language Website in CodeIgniter 4 — Add internationalization support to reach a global audience
- CodeIgniter 4 vs Laravel 12 — Compare both frameworks to pick the right one for your project
10 Conclusion
Next Steps to Enhance Your Blog:
- Add Update and Delete — Implement full CRUD operations. See our CodeIgniter 4 REST API CRUD Tutorial for reference
- Add Authentication — Protect the create/edit routes with login. Follow our CodeIgniter 4 Authentication Tutorial
- Add Role-Based Access — Separate admin and user permissions using our Role-Based Login System Guide
- Improve Security — Add input validation, CSRF protection, and SQL injection prevention
- Add Pagination — Use CodeIgniter 4's built-in pagination library for large post collections
- Optimize Performance — Implement caching with our Redis Cache in CodeIgniter 4 Tutorial
- Add Multi-Language Support — Make your blog multilingual with our CI4 i18n Guide
This starter project gives you a solid foundation to understand CodeIgniter 4's MVC architecture. Keep building and experimenting — the best way to learn is by doing!
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
CodeIgniter 4 is a modern, lightweight PHP framework that follows the Model-View-Controller (MVC) architectural pattern. It is known for its small footprint, fast execution speed, and minimal configuration requirements. CodeIgniter 4 is ideal for building dynamic web applications like blogs, e-commerce sites, and REST APIs, and it includes built-in features like database migrations, CLI tools (spark), CSRF protection, and environment-based configuration.
Yes, Composer is the recommended and easiest way to install CodeIgniter 4, as it automatically handles all dependencies and autoloading. You can install a new project by running: composer create-project codeigniter4/appstarter project-name. While manual installation is possible by downloading from GitHub, Composer ensures you can easily update the framework and manage third-party packages.
The simplest approach is to include Bootstrap 5 CDN links (CSS and JS) in your base layout file (e.g., app/Views/layout.php), as demonstrated in this tutorial. Alternatively, you can install Bootstrap via npm or download the files locally into your public directory for offline use. Using the CDN approach is recommended for quick prototyping and small projects.
Yes, you can add user authentication in several ways. The easiest option is to integrate CodeIgniter Shield, which is the official authentication library for CodeIgniter 4 and provides login, registration, password reset, and role-based access out of the box. Alternatively, you can build a custom authentication system using sessions and database-backed user credentials. Check our CodeIgniter 4 Authentication Tutorial for a detailed walkthrough.
This is a starter/demo project designed for learning purposes. For a production-ready blog, you should add several enhancements: server-side input validation using CodeIgniter 4 validation library, user authentication and authorization, CSRF token verification (already included via csrf_field()), pagination for listing posts, proper error handling, security headers, output escaping to prevent XSS attacks, and SEO-friendly URL structures. Our related tutorials cover many of these production requirements.
To add Update (Edit) functionality, create an edit() method in your Blog controller that loads a post by its slug, displays a pre-filled form, and updates the record on submission using the model update() method. For Delete, add a delete() method that removes the post by ID. Make sure to add corresponding routes in Routes.php. For a complete CRUD example with all four operations, refer to our CodeIgniter 4 REST API CRUD Tutorial.
A blank page usually indicates a PHP error that is being suppressed. First, ensure your .env file has CI_ENVIRONMENT set to development (not production) so errors are displayed. Also check the writable/logs/ directory for log files containing error details. Common causes include incorrect database credentials, missing PHP extensions (like mysqlnd or intl), or syntax errors in your code.
CodeIgniter 4 has a built-in Pager library. In your Blog controller index() method, replace findAll() with paginate(10) to show 10 posts per page. Then pass the pager object to the view: $data["pager"] = $this->postModel->pager. In your view file, add the pager links() method wherever you want the pagination links to appear. Bootstrap-styled pagination is supported out of the box.
