[!TIP]
Symfony 6.4 and newer make use of the AssetMapper component rather than Webpack Encore - An updated version of this post using AssetMapper can be found here
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 web app 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 will also need the Symfony Webpack Encore bundle to provide the front-end integration. By default, new Symfony webapp projects (created with symfony new --webapp myapp) use AssetMapper. To switch to Webpack Encore run
composer remove symfony/ux-turbo symfony/asset-mapper symfony/stimulus-bundle
composer require symfony/webpack-encore-bundle symfony/ux-turbo symfony/stimulus-bundle
Install node modules
These are the node modules that are required by the Encore bundle to provide 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
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();
Creating our first-page using Tailwind CSS
Now that we have got the setup done and out of the way, we can start to build our first page. Let's 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. Let's 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 %}
<div class="relative overflow-hidden bg-white">
<div class="pb-80 pt-16 sm:pb-40 sm:pt-24 lg:pb-48 lg:pt-40">
<div class="relative mx-auto max-w-7xl px-4 sm:static sm:px-6 lg:px-8">
<div class="sm:max-w-lg">
<h1 class="text-4xl font-bold 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:mx-auto lg:w-full lg:max-w-7xl">
<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="grid flex-shrink-0 grid-cols-1 gap-y-6 lg:gap-y-8">
<div class="h-64 w-44 overflow-hidden rounded-lg sm:opacity-0 lg:opacity-100">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-01.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
<div class="h-64 w-44 overflow-hidden rounded-lg">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-02.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
</div>
<div class="grid flex-shrink-0 grid-cols-1 gap-y-6 lg:gap-y-8">
<div class="h-64 w-44 overflow-hidden rounded-lg">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-03.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
<div class="h-64 w-44 overflow-hidden rounded-lg">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-04.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
<div class="h-64 w-44 overflow-hidden rounded-lg">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-05.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
</div>
<div class="grid flex-shrink-0 grid-cols-1 gap-y-6 lg:gap-y-8">
<div class="h-64 w-44 overflow-hidden rounded-lg">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-06.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
<div class="h-64 w-44 overflow-hidden rounded-lg">
<img src="https://tailwindui.com/plus/img/ecommerce-images/home-page-03-hero-image-tile-07.jpg" alt="" class="h-full w-full object-cover object-center">
</div>
</div>
</div>
</div>
</div>
<a href="#" class="inline-block rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-center 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 favourite browser and go to http://localhost:8080
and see the result. It should look something like this
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
- Tailwind CSS for Symfony! - https://symfony.com/bundles/TailwindBundle/current/index.html
- Get started with Tailwind CSS - Installation - https://tailwindcss.com/docs/installation
- Install Tailwind CSS with Laravel - https://tailwindcss.com/docs/guides/laravel
- Tailwind UI - Official Tailwind CSS Components & Templates - https://tailwindui.com/
Originally published at https://chrisshennan.com/blog/using-tailwind-css-with-symfony-asset-mapper