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.

Note: This is correct, real CodeIgniter 4 code meant to be copied into your own project's 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 controllerAvailable 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>&copy; <?= 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
Rule of thumb: if a value in your view came from a database row, a form submission, a query-string parameter, or anywhere else outside your own code, wrap it in 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.

Next: Views need data, and that data usually comes from the database. Continue with Models & Query Builder to see how CodeIgniter fetches, inserts, and updates rows behind the scenes.