Compiling and Serving Frontend Assets in Flask
Tags: flask, python, software, frontend
Published: 2020-03-18 02:49:48

---
title: Compiling and Serving Frontend Assets in Flask
published: true
date: 2020-03-18 01:22:00 UTC
tags: Flask,Python,Software,Frontend
series: Build Flask Python Apps
canonical_url: https://hackersandslackers.com/flask-assets/
---

 ![Compiling and Serving Frontend Assets in Flask](https://hackersandslackers-cdn.storage.googleapis.com/2020/03/flask-assets.jpg)

You’re probably not a front-end developer, and I’m guessing you have no desire to become one. I’m fairly confident of this because you’re currently engaged in a Flask tutorial, as opposed to complaining about the number of frontend frameworks you’ve been forced to learn in the last decade (sorry frontend fam,   you know I love you ).

Every backend dev needs to come to terms with a stark reality: meaningful apps can’t be built without eventually digging into writing some styles or frontend JavaScript. Since the early days of Django, Python developers have opted to coin frontend code as static assets: a backhanded term that seemingly downplays the importance of JavaScript, CSS, or any presentation layer assets which make up a web application. It had been decreed that only two types of code exist in the universe: Python, and not-Python. Anything that isn't a **.py** file is considered to be a "static" asset, tucked into a single directory after presumably receiving a handoff from a frontend developer. This implied mindset is both somewhat absurd yet also unsurprising.

It’s time to face our fears of dealing with frontend code in Flask the **right** way: by writing styles in CSS preprocessors, minifying files, and serving them in “bundles” akin to the Webpack equivalent. If you’re the type of person who’d prefer to link to a CDN-hosted version of jQuery 1.11 or include a hideously generic Bootstrap library to style your app for you, please stop reading and go away. For those of us with higher aspirations than a full-time job maintaining 30-year-old legacy code for Citibank, we’ll be learning about [Flask-Assets](https://flask-assets.readthedocs.io/en/latest/).

## How it Works

Flask applications utilizing the Flask-Assets library will generally have two directories related to “static” assets. One directory is reserved for raw uncompressed code, while the other is for production-ready, compressed assets generated from files stored in the former (this production-ready directory is commonly referred to as a **/dist** or **/build** folder). Depending on how we configure our app, Flask-Assets will compile static assets into bundles upon startup. This is accomplished with the help of a few auxiliary Python libraries dedicated to building and compressing files: [lesscpy](https://pypi.org/project/lesscpy/) and [pyscss](https://pypi.org/project/pyScss/) compile LESS/SCSS to CSS respectively, while [cssmin](https://pypi.org/project/cssmin/) and [jsmin](https://pypi.org/project/jsmin/) compress CSS/JavaScript.

Depending on our choice of stack, we’ll need to install the appropriate Python packages. I plan on writing styles with LESS and adding some frontend JavaScript to my app:

```shell
$ pip install flask-assets lesscpy cssmin jsmin
```

### Flask-Assets Configuration

As with most Flask plugins, we can configure Flask-assets by setting variables in our top-level Flask config. Whoever created this library made the interesting choice of _not documenting any of this_, so I'm going to save you some time:

```python
LESS_BIN = '/usr/local/bin/lessc'
ASSETS_DEBUG = False
ASSETS_AUTO_BUILD = True
```
<figcaption>config.py</figcaption>

- **LESS\_BIN** : LESS requires the npm library called [less](https://www.npmjs.com/package/less) (known to the system as _lessc_) to be installed on our system in order to compile LESS into CSS. Run `which lessc` in your terminal to find this path. The SASS equivalent to this is **SASS\_BIN**.
- **ASSETS\_DEBUG** : We're bound to screw up a style or JS function every now and then, which sometimes doesn't become obvious until we build our bundles and try to use our app. If we set this variable to `True`, Flask-Assets _won't_ bundle our static files while we're running Flask in debug mode. This is useful for troubleshooting styles or JavaScript gone wrong.
- **ASSETS\_AUTO\_BUILD** : A flag to tell Flask to build our bundles of assets when Flask starts up automatically. We generally want to set this to be **True** , unless you'd prefer only to build assets with some external force (AKA: you're annoying).

## Creating Asset Bundles

Before diving into code, let's establish our app's structure. Nothing out of the ordinary here:

```shell
app
├── /static
│   ├── /img
│   ├── /dist
│   │   ├── /css
│   │   └── /js
│   └── /src
│       ├── /js
│       └── /less
├─ /templates
├─ config.py
└─ app.py
```
<figcaption>Our project structure.</figcaption>

The key takeaway is that we're going to set up Flask-Assets to look for raw files to compile in **static/src/less** and **static/src/js**. When the contents of these folders are compiled, we'll output the results to **static/dist/less** and **static/dist/js** where they'll be ready to serve.

Let's get this baby fired up in **app.py** :

```python
from flask import Flask
from flask_assets import Environment


app = Flask(__name__, instance_relative_config=False)

assets = Environment(app)
```
<figcaption>app.py</figcaption>

We initialize Flask-Assets by creating an **Environment** instance, and initializing it against Flask's **app** object. There's an alternative way of initializing the library via the Flask application factory, but we'll get there in a moment.

Our first asset "bundle" is going to come from LESS files found in our **static/src/less** directory. We're going to compile these files into CSS, minify the resulting CSS, and output it for production:

```python
from flask import Flask
from flask_assets import Environment, Bundle


app = Flask(__name__, instance_relative_config=False)

assets = Environment(app)
style_bundle = Bundle('src/less/*.less',
                     filters='less,cssmin',
                     output='dist/css/style.min.css',
                     extra={'rel': 'stylesheet/css'})
assets.register('main_styles', style_bundle)  # Register style bundle
style_bundle.build()  # Build LESS styles
```
<figcaption>app.py</figcaption>

The **Bundle** object is where most of our magic happens. Bundle can accept any number of positional arguments, where each argument is the path of a file to include in our bundle. Our example creates a bundle from `'src/less/*.less'`, which means "all **.less** files in **src/less**." We could instead pass individual files (like `'src/less/main.less', 'src/less/variables.less'`, etc) if we want to cherry-pick specific stylesheets. In this scenario, it's important to note that any imports within these LESS files (such as `@import './other_styles.less'`) would automatically be included in our bundle as well.

The next step in creating our **Bundle** is passing some necessary keyword arguments:

- **Filters**: This is where we tell Flask-Assets which Python libraries to use to handle compiling our assets. Passing `'less,cssmin'` tells our Bundle that we're going to build a bundle from LESS files using **lesscpy** , which we should then compress using **cssmin**.
- **Output**: The directory where our production-ready bundle will be saved and served from.
- **Extra**: Appends additional HTML attributes to the bundle output in our Jinja template (this usually isn't necessary).

After creating a bundle of styles called **style\_bundle** , we "register" the bundle with our environment with `assets.register('main_styles', style_bundle)` ( `'main_styles'` is the arbitrary name we assign to our bundle, and `style_bundle` is the variable itself).

We finally build our bundle with `style_bundle.build()`. This outputs a single CSS file to **dist/css/style.css**.

### Creating a JavaScript Bundle

The process for creating a bundle for frontend JavaScript is almost identical to to the bundle we created for styles. The main difference is our usage of the **jsmin** filter to minimize the JS files.

Here's an example of creating bundles for both in unison:

```python
...

style_bundle = Bundle('src/less/*.less',
                     filters='less,cssmin',
                     output='dist/css/style.min.css',
                     extra={'rel': 'stylesheet/css'})
js_bundle = Bundle('src/js/main.js',
                   filters='jsmin',
                   output='dist/js/main.min.js')
assets.register('main_styles', style_bundle)
assets.register('main_js', js_bundle)
style_bundle.build()
js_bundle.build()
```

### Serving Bundles via Page Templates

We can serve our newly created assets in our page templates as we would any other static asset:

```jinja2
{% extends "layout.jinja2" %}

<link href="{{ url_for('static', filename='dist/css/style.min.css') }}" rel="stylesheet" type="text/css">
<script src="{{ url_for('static', filename='dist/js/main.min.js') }}"></script>
```

Launching your Flask app should now result in styles and JavaScript being built to **src/dist**. If all went well, your page templates will pick these up and your app will be looking snazzy with some sexy, performance-optimized frontend assets.

#### PROTIP!
There's apparently an alternative way to create bundles entirely in templates with zero Python code. I personally haven't tried this out, but the simplicity of the syntax is pretty f'in cool: 
   
```jinja2
{% assets filters="jsmin", output="src/js/main.js", "src/js/jquery.js"  %}

<script type="text/javascript" src="{{ ASSET_URL }}"></script>
    
{% endassets %}
```

<!--kg-card-end: html-->
## Blueprint-specific Bundles

One of the most powerful features of Flask-assets is the ease with which we can create Blueprint-specific asset bundles. This allows us to "code-split" relevant frontend assets to be exclusive to Blueprints, primarily for performance purposes. We're going to build on the example project we created last time in our [Flask Blueprints tutorial](https://hackersandslackers.com/flask-blueprints/) to create blueprint-specific bundles.

Here's a quick overview of a project with two registered blueprints (/main and /admin) which we'll create asset bundles for:

```shell
flask-assets-tutorial
├── /flask_assets_tutorial
│   ├── __init__.py
│   ├── assets.py
│   ├── /admin
│   │   ├── admin_routes.py
│   │   ├── /static
│   │   └── /templates
│   ├── /main
│   │   ├── main_routes.py
│   │   ├── /static
│   │   └── /templates
│   ├── /static
│   └── /templates
├── config.py
└── wsgi.py
```

Each of our blueprints has its own **static** folder, which is intended to contain blueprint-specific assets. Our app still has a **static** folder of its own, containing assets to be shared across all blueprints. Before getting into the details of how that works, let's look at how we instantiate Flask-assets in the Flask application factory:

```python
"""Initialize app."""
from flask import Flask
from flask_assets import Environment
from .assets import compile_assets

assets = Environment()


def create_app():
    """Construct the core application."""
    app = Flask(__name__, instance_relative_config=False)
    app.config.from_object('config.Config')

    # Initialize plugins
    assets.init_app(app)

    with app.app_context():
        # Import parts of our application
        from .admin import admin_routes
        from .main import main_routes
        app.register_blueprint(admin_routes.admin_bp)
        app.register_blueprint(main_routes.main_bp)
        compile_assets(assets)

        return app
```
<figcaption> __init__.py</figcaption>

Let's check out what's happening in **assets.py** :

```python
"""Compile static assets."""
from flask_assets import Bundle


def compile_assets(assets):
    """Configure and build asset bundles."""
    main_style_bundle = Bundle('src/less/*.less',
                               'main_bp/homepage.less',
                               filters='less,cssmin',
                               output='dist/css/landing.css',
                               extra={'rel': 'stylesheet/css'})
    main_js_bundle = Bundle('src/js/main.js',
                            filters='jsmin',
                            output='dist/js/main.min.js')
    admin_style_bundle = Bundle('src/less/*.less',
                                'admin_bp/admin.less',
                                filters='less,cssmin',
                                output='dist/css/account.css',
                                extra={'rel': 'stylesheet/css'})
    assets.register('main_styles', main_style_bundle)
    assets.register('main_js', main_js_bundle)
    assets.register('admin_styles', admin_style_bundle)
    main_style_bundle.build()
    main_js_bundle.build()
    admin_style_bundle.build()
```
<figcaption>assets.py</figcaption>

The magic comes in to play in `Bundle()`'s positional arguments. In the case of **main\_style\_bundle** , we pass global styles with `src/less/*.less` and _blueprint specific_ styles with `main_bp/homepage.less`. But wait, **main\_bp** isn't a directory! In fact, it's the registered name of our main blueprint that we create in **main\_routes.py** :

```python
main_bp = Blueprint(
    'main_bp',
    __name__,
    template_folder='templates',
    static_folder='static'
)  # Blueprint Configuration
```
<figcaption>main_routes.py</figcaption>

When setting `static_folder='static'` on our blueprint, we're telling Flask to check for assets in _two_ places: first in **flask\_assets\_tutorial/static** , and then **flask\_assets\_tutorial/main/static** second. `Bundle()` recognizes that `main_bp` is a blueprint name as opposed to a directory and thus resolves the asset correctly.

Setting up blueprint-specific assets isn't a walk in the park the first time around. The official documentation for Flask-assets is simply god-awful. As a result, there are plenty of pitfalls and quirks that can ruin somebody's day... especially mine. If you find yourself getting stuck, I've set up a working project on Github here:

{% github https://github.com/hackersandslackers/flask-assets-tutorial %}