Using Tailwind CSS with Symfony Encore

7th July 2022

Why Tailwind CSS?

If you are reading this, you are probably in the same situation as me i.e. you can do backend development without breaking a sweat, but when you try your hand at some front-end development, it ends up looking like a throwback to 1994.

I've tried bootstrap and various themes, and whilst they help, they tend to focus on a handful of specific page templates and deviating from them leaves me in the same situation as before.

Tailwind, on the other hand, has a collection of components that you can piece together, any way you want, to build up your own templates and spans everything from marketing pages to an e-commerce store and checkout pages to application UI, meaning you can get your product up and running with a beautiful looking front-end with little effort and leaving you more time to do the stuff you are good at.

The downside is you need to pay for the components - currently £219 + VAT, but in my view, it is well worth it as it includes

  • All the components to build your marketing, application or e-commerce sites
  • Lifetime membership - free updates forever
  • Unlimited projects - no need to purchase for each project
  • Pre-made templates using Tailwind CSS ready to use - this is something they have just recently added.

I've spent much more than that over the years on templates that I've never quite managed to get working, whilst my first attempt at a project using Tailwind (https://passwordangel.co) had the front-end up and running inside an hour.

You can see all the available components and templates on the Tailwind UI site - https://tailwindui.com/

So without further ado, let's get stuck into getting Tailwind CSS up and running in your Symfony project.

Setup

Install Symfony

In this example, I'm going to set up a webapp symfony project because I want all the typical components for a web application (twig, routing, etc)

symfony new --webapp symfony-tailwind-demo
cd symfony-tailwind-demo
composer update

I'm also going to need the Symfony Webpack Encore bundle to provide the frontend integration.

composer require symfony/webpack-encore-bundle

Install node modules

These are the node modules that are required by the Encore bundle to provider the frontend functionality and the tailwind module to help build the CSS require for the templates that you build.

yarn add --dev \
    @hotwired/stimulus core-js \
    @symfony/stimulus-bridge \
    @symfony/webpack-encore \
    autoprefixer \
    postcss \
    postcss-loader \
    tailwindcss \
    webpack-notifier@^1.6.0 

And now initialise tailwind so it creates your tailwind.config.js and postcss.config.js files

yarn tailwindcss init -p

Configuration

We need to specify which files to watch for changes in the /tailwind.config.js file. In our case, we want to watch for changes to any of the twig template files

// tailwind.config.js
module.exports = {
  content: [
      "./templates/**/*.twig"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

We also need to enable the post CSS loader in the webpack.config.js so it can resolve the tailwind CSS directives (we will get to that in a minute) into the desired CSS.

// webpack.config.js
Encore
    // directory where compiled assets will be stored
    .setOutputPath('public/build/')

   // ... rest of the default configuration ...

    // Add this line to enable the post CSS loader
    .enablePostCssLoader()
;

module.exports = Encore.getWebpackConfig();

Encore.enablePostCssLoader();

Creating our first page using Tailwind CSS

Now that we have got the setup done and out the way, we can start to build our first page. Lets start by adding the tailwind CSS directives to our CSS file

/* /assets/styles/app.css */

@tailwind base;
@tailwind components;
@tailwind utilities;

and then adding the encore_entry_link_tags and encore_entry_script_tags twig functions to our base template so encore can load in the CSS and javascript

{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">

        {% block stylesheets %}
            {{ encore_entry_link_tags('app') }}
        {% endblock %}

        {% block javascripts %}
            {{ encore_entry_script_tags('app') }}
        {% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
    </body>
</html>

Next, we want to add a routing file so we've got an entry point into the application

# config/routes.yaml
index:
    path: /
    controller: App\Controller\DefaultController::index

and then the corresponding controller

<?php

// src/Controller/DefaultController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class DefaultController extends AbstractController
{
    /**
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function index()
    {
        return $this->render('default/index.html.twig', []);
    }
}

And now the view template. Lets take a component from the preview section from https://tailwindui.com/preview. In this case I'm going to use "Ecommerce -> Components -> Promo Sections -> With image tiles"

{% extends 'base.html.twig' %}

{% block body %}
  <!-- This example requires Tailwind CSS v2.0+ -->
  <div class="relative bg-white overflow-hidden">
    <div class="pt-16 pb-80 sm:pt-24 sm:pb-40 lg:pt-40 lg:pb-48">
      <div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 sm:static">
        <div class="sm:max-w-lg">
          <h1 class="text-4xl font font-extrabold tracking-tight text-gray-900 sm:text-6xl">Summer styles are finally here</h1>
          <p class="mt-4 text-xl text-gray-500">This year, our new summer collection will shelter you from the harsh elements of a world that doesn't care if you live or die.</p>
        </div>
        <div>
          <div class="mt-10">
            <!-- Decorative image grid -->
            <div aria-hidden="true" class="pointer-events-none lg:absolute lg:inset-y-0 lg:max-w-7xl lg:mx-auto lg:w-full">
              <div class="absolute transform sm:left-1/2 sm:top-0 sm:translate-x-8 lg:left-1/2 lg:top-1/2 lg:-translate-y-1/2 lg:translate-x-8">
                <div class="flex items-center space-x-6 lg:space-x-8">
                  <div class="flex-shrink-0 grid grid-cols-1 gap-y-6 lg:gap-y-8">
                    <div class="w-44 h-64 rounded-lg overflow-hidden sm:opacity-0 lg:opacity-100">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-01.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                    <div class="w-44 h-64 rounded-lg overflow-hidden">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-02.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                  </div>
                  <div class="flex-shrink-0 grid grid-cols-1 gap-y-6 lg:gap-y-8">
                    <div class="w-44 h-64 rounded-lg overflow-hidden">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-03.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                    <div class="w-44 h-64 rounded-lg overflow-hidden">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-04.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                    <div class="w-44 h-64 rounded-lg overflow-hidden">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-05.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                  </div>
                  <div class="flex-shrink-0 grid grid-cols-1 gap-y-6 lg:gap-y-8">
                    <div class="w-44 h-64 rounded-lg overflow-hidden">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-06.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                    <div class="w-44 h-64 rounded-lg overflow-hidden">
                      <img src="https://tailwindui.com/img/ecommerce-images/home-page-03-hero-image-tile-07.jpg" alt="" class="w-full h-full object-center object-cover">
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <a href="#" class="inline-block text-center bg-indigo-600 border border-transparent rounded-md py-3 px-8 font-medium text-white hover:bg-indigo-700">Shop Collection</a>
          </div>
        </div>
      </div>
    </div>
  </div>
{% endblock %}

Everything should now be in place and you can set encore running and watching for changes by running this in a terminal window.

yarn encore dev --watch

While this is running, you can modify any of the CSS classes in the above template i.e. change text-4xl to text-6xl and you should see notifications on the CSS being rebuilt in the terminal window.

Viewing your first page

We need to actually see the page to verify it is working as we expect. For simplicity, I'm going to use PHP's built-in webserver in this example so we can run, in another terminal window

cd public
php -S localhost:8080

And now we can open our favorite browser and go to http://localhost:8080 and see the result. It should look something like this

Symfony project using TailwindCSS

Try before you buy

So you can see this in action and experiment for yourself, I've created a git repository with the step of this blog post already completed so you can simply git clone and install the dependencies and away you go.

Git repository: https://github.com/chrisshennan/symfony-and-tailwindcss-example

References

Liked this article?

Subscribe to my newsletter and get the ramblings of a wannabe #indiehacker straight in your inbox once a month. Ramblings will cover a variety of topics including:-

Web Development, DevOps, Startups, Bootstrapping, #buildinpublic, SEO, opinion and personal experience.

I won't send you spam and you can unsubscribe at any time.