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.

How to Build a Simple Blog in CodeIgniter 4 with MySQL and Bootstrap Tutorial

Table Of Content

1 Prerequisites

Before starting this tutorial, make sure you have the following installed and configured on your system:
  • 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

Building a blog from scratch is one of the best ways to learn a PHP framework. A blog project covers all the fundamental concepts — database interaction, routing, controllers, views, and form handling — making it an ideal learning exercise.

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

First, make sure your computer has a composer.
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

Rename the env file to .env and set the development mode in the .env file also configure mysql:

# 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

We'll use a simple migration. Run:
    
        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

Generate 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 PageNotFoundException to 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

Install Bootstrap via CDN (or npm if preferred).
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

In app/Config/Routes.php, define routes for the Blog controller:


$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 posts
  • GET /blog/create — Shows the form to create a new post
  • POST /blog/create — Handles form submission and saves the post
  • GET /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

Start the built-in CodeIgniter development server using the following command:

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 .env file 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
If you found this tutorial helpful, explore more CodeIgniter 4 guides on our site to take your project further:

10 Conclusion

In this tutorial, we learned how to build a simple blog using CodeIgniter 4, MySQL, and Bootstrap 5. We covered the complete workflow — from project installation and environment configuration to creating database migrations, models, controllers, views, and routes. You now have a functional blog that can list posts, display individual posts, and create new entries.

Next Steps to Enhance Your Blog:
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!
Revathi M - PHP and CodeIgniter Developer

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.