CodeIgniter Views
A view is the "V" in MVC — the part of your application responsible purely for
presentation. In CodeIgniter 4, views live in app/Views/ and are simply
plain PHP files. There is no separate templating language to learn (no Twig,
no Blade-style syntax) — just ordinary HTML mixed with PHP's short-echo <?= ?>
tags and colon-syntax control structures. If you're comfortable writing PHP inside HTML from the
Core PHP track, you already know most of what you need for CodeIgniter views.
1. What a View File Looks Like
A view is nothing more than an HTML document with small bursts of PHP dropped in wherever dynamic
content is needed. Below is a typical view for showing a single article,
saved as app/Views/articles/show.php:
<!DOCTYPE html>
<html>
<head>
<title><?= esc($article['title']) ?></title>
</head>
<body>
<h1><?= esc($article['title']) ?></h1>
<p><?= esc($article['content']) ?></p>
</body>
</html>
Notice there is no class, no namespace, and no boilerplate — a view is
simply expected to output HTML. Every variable used inside it (like $article above)
must be supplied by the controller that renders the view.
app/Views/ folder. This website itself doesn't have CodeIgniter installed, so nothing on this page actually executes.2. Rendering a View from a Controller
Views are never opened directly by the browser — they are rendered by a controller
using the global view() helper function, which is available everywhere in CodeIgniter 4
without any extra setup. As covered on the Controllers page,
a controller method typically ends by returning the result of view():
<?php
namespace App\Controllers;
class Articles extends BaseController
{
public function show($id)
{
$model = new \App\Models\ArticleModel();
$data['article'] = $model->find($id);
return view('articles/show', $data);
}
}
The first argument to view() is the view's path relative to app/Views/,
without the .php extension and using a forward slash for subfolders — so
'articles/show' maps straight to app/Views/articles/show.php. The second
argument is the data you want available inside that view.
3. Passing Data as an Associative Array
CodeIgniter takes the associative array you pass as the second argument to view() and
extracts it, so each array key becomes a plain PHP variable inside the view. This
keeps the view file itself simple — it never needs to know about $data as an array,
only about the individual variables it expects.
| In the controller | Available inside the view as |
|---|---|
$data['article'] = $row; | $article |
$data['pageTitle'] = 'Home'; | $pageTitle |
$data['articles'] = $rows; | $articles (an array) |
You can build the array in one shot or add to it piece by piece before calling view():
$data = [
'pageTitle' => 'Latest Articles',
'articles' => $model->findAll(),
];
return view('articles/index', $data);
4. Reusable View Partials (Header, Footer, Templates)
Repeating the same <head>, navigation bar, and footer markup in every single view
file quickly becomes unmaintainable. CodeIgniter solves this the same simple way plain PHP does:
since view() just renders a PHP file and returns a string, you can call it
again from inside another view to include a shared fragment. A common convention is to
keep these fragments in app/Views/templates/.
app/Views/templates/header.php
<!DOCTYPE html>
<html>
<head>
<title>My Site</title>
</head>
<body>
<header>
<h1>My Site</h1>
</header>
app/Views/templates/footer.php
<footer>
<p>© <?= date('Y') ?> My Site</p>
</footer>
</body>
</html>
app/Views/articles/index.php (using both fragments)
<?= view('templates/header') ?>
<h2><?= esc($pageTitle) ?></h2>
<p>Page-specific content goes here.</p>
<?= view('templates/footer') ?>
Nothing here is CodeIgniter-specific magic — view('templates/header') simply renders
that file and returns its output as a string, which the outer <?= ?> then echoes
in place. CI4 also ships an optional, more powerful View Cells and layout system for larger
projects, but plain fragment includes like this cover the vast majority of real sites.
5. Escaping Output Safely with esc()
Any value that ultimately came from user input — form fields, URL segments, database rows populated
by users — must never be echoed into HTML raw. Doing so opens the door to
Cross-Site Scripting (XSS): if a visitor's "name" field contains
<script>...</script>, echoing it unescaped lets that script run in every
other visitor's browser. CodeIgniter ships a built-in global helper function, esc(),
specifically to make safe output the path of least resistance.
// Unsafe — never do this with user-supplied data
echo $comment;
// Safe — HTML-encodes special characters before output
echo esc($comment);
// Short-echo form, the way you'll write it inside a view
<?= esc($comment) ?>
By default esc() encodes for an HTML context, the same job PHP's
htmlspecialchars() does — but it can also escape correctly for other contexts by
passing a second argument, which matters because HTML-encoding alone is not safe inside a
<script> block, a CSS value, or a URL:
<?= esc($title, 'html') ?> // default — plain HTML text
<?= esc($slug, 'url') ?> // safe inside a URL / href
<?= esc($value, 'js') ?> // safe inside an inline <script>
<?= esc($value, 'css') ?> // safe inside a <style> value
esc() before echoing it. Values you generated yourself (a hard-coded page title, a loop counter) don't need it, but it never hurts.6. Worked Example: Looping Over Data in a View
Bringing it all together, here's a realistic app/Views/articles/index.php that receives
an $articles array from the controller and loops over it with a foreach,
escaping every piece of user-controlled text along the way. Views conventionally use the
colon (alternate) syntax for control structures instead of curly braces, since it
reads more cleanly when mixed with HTML:
<h1>Latest Articles</h1>
<?php if (empty($articles)): ?>
<p>No articles have been published yet.</p>
<?php else: ?>
<ul class="article-list">
<?php foreach ($articles as $article): ?>
<li>
<h2>
<a href="/articles/<?= esc($article['id'], 'url') ?>">
<?= esc($article['title']) ?>
</a>
</h2>
<p><?= esc($article['excerpt']) ?></p>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
Every dynamic value that reaches the browser — the title, the excerpt, even the article ID used
inside the href attribute — passes through esc() with the appropriate
context. This one habit closes off the most common XSS vector in template code.