In this post, we will learn how to use the AdonisJS template engine (Edge) and set up Webpack/Laravel mix to process TailwindCSS and Alpine.js.
Views & Templates
The official and the recommended template engine of AdonisJS is Edge. It is a logical template engine and comes with some neat features like:
- An easy to write syntax
- Support for conditionals, loops, layouts, and partials
- Support for components (a personal favorite)
- Allows runtime debugging using Chrome DevTools
- Accurate stack traces
We won't be covering every feature of the edge in this series and just focus on the pieces we need for our todo app.
Rendering views
Open the start/routes.ts
file and replace all of its contents with the following code snippet.
import Route from '@ioc:Adonis/Core/Route'
Route.get('/', 'TodosController.index')
Assuming you already have the TodosController
from the previous post. Replace its contents with the following code snippet.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class TodosController {
public async index({ view }: HttpContextContract) {
return view.render('todos/index')
}
}
- The
view.render
methods takes the path to the template file stored inside theresources/views
directory. - Adding a file extension is optional.
- The return value is a string. In our case, it will be a string containing the final HTML.
You can also render views using the Adonis/Core/View
module directly. For example:
import View from '@ioc:Adonis/Core/View'
View.render('todos/index')
However, there is a difference between using the module directly and using the ctx.view
object.
The ctx.view
object also contains the information about the current HTTP request like the request details, authenticated user, session flash messages and so on. Therefore it is recommended to always use ctx.view
during an HTTP request.
Creating the view file
Let's create the todos/index.edge
file using the following ace command.
node ace make:view todos/index
# CREATE: resources/views/todos/index.edge
Open the newly created file and paste the following contents inside it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
</head>
<body>
<h2>Todos app</h2>
<p>We will being rendering todos here</p>
</body>
</html>
Finally, visit the http://localhost:3333 URL to view the rendered HTML.
Serving static assets
Everything we have done so far is on the server-side. However, we do need some way to style our webpages using CSS and also write front-end JavaScript to make the pages interactive.
Let's start with the basics and slowly move towards using a build tool like Webpack to bundle the front-end assets.
To begin with, we need some way to serve CSS and JavaScript files to the browser. In AdonisJS, you need to keep these files inside the public
folder and then access them using the relative path. Let's give it a try.
Create a new file named styles.css
inside the public
directory and paste the following contents to it.
body {
background: #d7dceb;
background: -webkit-linear-gradient(to right, #d7dceb, #e2e2e2);
background: linear-gradient(to right, #d7dceb, #e2e2e2);
}
Now, visit http://localhost:3333/styles.css to access the file inside the browser.
Great! Lets open resources/views/todos/index.edge
and load this css file.
<head>
<!-- Other tags -->
<link rel="stylesheet" href="/styles.css" />
</head>
The same process can be repeated for JavaScript files, images, fonts, and so on.
Using assets bundler
In the previous section, we accomplished the task of serving static files by adding them to the public
folder. Of course, these files have to be written in a way that browsers can understand, parse, and execute.
However, we live in a complicated world. We are so much used to using pre/post processors. Writing code that browsers cannot fully understand. Therefore we need tools like Webpack to compile our version of code into something different browsers can understand.
Using Laravel Mix
The Laravel community (Jeffrey Way to be specific) created Laravel mix which wraps webpack into a high level, less verbose API. Even though the package uses the keyword Laravel
, it can be used independently as well.
Luckily, there is also a package for AdonisJS that eases the setup process of using Laravel mix in AdonisJS apps.
So let's start by installing it.
npm i --save-dev adonis-mix-asset laravel-mix@next
Run the following command to configure the package.
node ace invoke adonis-mix-asset
# CREATE: webpack.mix.js
# UPDATE: .adonisrc.json { commands += "adonis-mix-asset/build/commands" }
# UPDATE: .adonisrc.json { providers += "adonis-mix-asset" }
# CREATE: ace-manifest.json file
And start the compilation process by running the following ace command. Also, feel free to reference the README file of the adonis-mix-asset package.
node ace mix:watch
Setup TailwindCSS
We are all set now! Before giving this set up a test run let's also install and configure Tailwind CSS.
npm i -D tailwindcss
# Create a Tailwind config file
npx tailwindcss init
Open the webpack.mix.js
file and add the following line of code inside it.
mix.postCss('resources/css/styles.css', 'public/css', [require('tailwindcss')])
- Here we tell mix to process the
resources/css/styles.css
file using PostCSS. - The output should be written to the
public/css
folder. - Also, we are using tailwind as a plugin of PostCSS.
Why resources folder and not public?
If you noticed we are telling PostCSS to read the file from the resources/css
folder and not the public folder.
The PostCSS syntax is not something the browsers can understand and hence there is no point in keeping this file inside the public
folder. Instead, we want the processed output to be in the public
folder.
This is true for every other file including the frontend JavaScript, images, and so on. Any asset that needs pre-processing should not be in the public
folder.
Let's remove everything we added to the public
folder earlier.
rm -r public/*
Create a new file named css/styles.css
inside the resources
directory and paste the following contents inside it.
@tailwind base;
@tailwind components;
@tailwind utilities;
Finally, we need to update our template to load the CSS file created by Laravel mix inside the public
folder.
<link rel="stylesheet" href="/css/styles.css" />
Let's give it a try now. Run the following commands to start the HTTP server and the mix process.
# Starts AdonisJS server
node ace serve --watch
# Inside another terminal session
node ace mix:watch
Now open the webpage inside the browser and for sure you will see TailwindCSS getting loaded on the page.
Setup Alpine.Js
Let's quickly follow the same process for setting up Alpine.js. Begin by installing the package.
npm install alpinejs
Create a JavaScript file named app.js
inside the resources/js
directory and paste the following contents inside it.
import 'alpinejs'
Next, tell mix to pre-process this file by adding the following line of code to the webpack.mix.js
file.
mix.js('resources/js/app.js', 'public/js')
Also, make sure to restart the node ace mix:watch
command for the mix to pick up the config file changes.
Finally, we can load the processed JavaScript file inside the head tag.
<script src="/js/app.js" defer></script>
To give it a test run, modify the body
tag as follows.
<body x-data="{}" x-init="() => alert('Alpine is ready')">
<h2>Todos app</h2>
<p>We will being rendering todos here</p>
</body>
Closing notes
Initially, I decided to even design the webpage in the post. However, we already covered a lot of ground, so let's move the design phase to the next post.
Today we learned about
- Rendering views using the
ctx.view.render
method. - Server front-end assets by keeping them inside the
public
folder. - Setup
adonis-mix-asset
to process the front-end assets and write them to thepublic
folder.
Top comments (3)
Thank you for the series. I appreciate your work on the framework.
Great series as always. You can also use mix helper
{{ mix('css/styles.css') }}
which comes to us with adonix-mix-asset package. Keep it up bro.Yup. I avoid the
mix
function to keep it transparent for the reader and later introduce it explaining the purpose of this over a hardcoded path. 🙂