Creating Dynamic Pages in Symfony 4

In my last story I’ve written about creating pages with Symfony 4 (click here to go). Reading that you can see some essential components you need to deal every day if you develop with Symfony and about some different ways of creating your pages (through controllers and actions).
The last story is not very complete because only looks for static content pages. Basically, you need to create your Response class using hardcoded HTML as a string. Of course, even for static html is not the best choice, so this story will extend the previous one.

What is a dynamic page?

This question is obvious for web developers, but let’s give a help the newbies!
A dynamic page is the content you get from a web application that can vary based on the parameters you send on the url or about some internal application logic.
The best example is creating a hello world page, adding some data based on the query parameters.

How to create a dynamic page then?

As basic requirement we need a controller to allow us to create actions of course. So, in the terminal…

$ bin/console make:controllerChoose a name for your controller class (e.g. AgreeableKangarooController):
> UserController
created: src/Controller/UserController.phpSuccess!Next: Open your new controller class and add some pages!

Your controller class is created! So next step is to implement a new action and set up a path for the router on it. Take as an example a helloAction($userName) where we will get the query parameter for the user name from the url Doe

// file: src/Controller/UserController.php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class UserController extends Controller
* @Route("/users", name="users_route")
public function helloAction(Request $request)
// first get the user's name
$userName = $request->query->get('name', 'unknown user');
// create the dynamic content
$content = sprintf('<h1>Hello %s</h1>', $userName);
// return the content
return new Response($content);

What you can find the code above is the UserController class code.
At the action helloAction we will get the Request object generated by the container and get from the query parameters the variable name and if the user does not send it we will define as a default value unknown user.
Then, after getting the user’s name, we build our dynamic HTML content and send back to the browser using the Response object.

At this point there is nothing new. You are building a dynamic content and return it to the browser.
Of course we can improve this code. For sure you don’t (at least you shouldn’t) keep different kinds of code mixed up… and we have here HTML and PHP in the same file that don’t owns the responsibility of rendering HTML. What if we could separate these two layers?

TWIG, the template engine

A template engine is an application module that transforms one input into content in a specific format. Twig is a template engine that for your view logic will render a final HTML ready to consume and send back to the user’s browser. More than the basic behaviours, it has a lot of helpers you can use on your view file, handle PHP objects, etc.…
Also is important to know that you can use TWIG in your non Symfony projects. Please find more about it at

Installing twig

Installing is very simple:

$ composer require twig

you should get as output:

./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Prefetching 3 packages 🎵
- Downloading (100%)
Package operations: 3 installs, 0 updates, 0 removals
- Installing twig/twig (v2.4.6): Loading from cache
- Installing symfony/twig-bridge (v4.0.6): Loading from cache
- Installing symfony/twig-bundle (v4.0.6): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 1 recipe (e6cd993d94cd6aeed2bfddaf813e8da2)
- Configuring symfony/twig-bundle (>=3.3): From
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]
Some files may have been created or updated to configure your new packages.
Please review, edit and commit them: these files are yours.

One next step I really recommend you to do, is to track all the changes done in your folder through GIT :

git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: composer.json
modified: composer.lock
modified: config/bundles.php
modified: symfony.lock
Untracked files:
(use "git add <file>..." to include in what will be committed)

What has been done here? Composer downloaded TWIG packages and executed the new recipes. Now your application contains new configs that allows you to manage some TWIG paths and other values and a folder called templates where you will be able to create all your pages to be rendered back to the browser. So, take a moment to commit those changes to be able to have a better look at the following steps.

Now that we have TWIG we can now asking to the controller to render a specific view. First, to show that Symfony is aware of this new dependency, let’s try to create a new Controller and see which changes our project will have:

$ bin/console make:controller

I’ve created a PagesController, and as I do have TWIG installed I can notice that the maker bundle has done a new step: Create a twig file to allow me to create the view layer code:

 created: src/Controller/PagesController.php
created: templates/pages/index.html.twig # this step right here

So, what’s in that file? How the controller class will render that specific view?
Let’s have a look first for the PagesController class:

* @Route("/pages", name="pages")
public function index()
return $this->render('pages/index.html.twig', [
'controller_name' => 'PagesController',

Our default code is calling a render function. This function grabs the view file injecting a variables array that you can create to allow you to make your dynamic pages even more dynamic. Finally, if you search for file templates/pages/index.html.twig you will find HTML with TWIG instructions that will be compiled and cached to be returned for your users when they request that route.


This was a very short story. What did we get this time?

  • A dynamic page is what you return depending on application logic and/or query string parameters from the URL.
  • You should have your HTML code in a separate folder structure.
  • Installing TWIG we will be able to build our application view layer.

Software Engineering Manager and Software Engineer | Server Side Trainer | Human stuff as a hobby.