Check out our Bootcamp Schedule View Schedule

Managing Front-End Assets in Vapor, Part 1

Josh Justice

When you use Vapor to create a website, Vapor handles generating the HTML for your web pages. However, to get the website ready for production, you’ll need to add Cascading Style Sheets (CSS) and JavaScript (JS) as well. Let’s look at some of the basics of handling CSS and JS in Vapor so that you can deliver a well-performing site to your users, and have code that is easy and enjoyable to work in.

First, we’ll set up custom CSS and JS files for your site, then add a third-party front-end library via a Content Delivery Network, then switch to hosting the files directly on your server. This post assumes you have some basic familiarity with CSS and JS. If they’re brand-new to you, you may want to check out the Mozilla Developer Network first.

Stylesheets

Create a new Vapor webapp project by running vapor new mysite --web from the command line. Run vapor xcode -y to generate and open an Xcode project, then click the Run button. Open http://localhost:8080/hello in a web browser. You should see a typical “Hello, World!” message.

Check Resources/views/base.leaf. Notice that there’s a <link> tag pointing to the file /styles/app.css. In your Xcode project, this file can be found at Public/styles/app.css.

In Vapor, the Public/ folder is used to store “static assets,” files that the web server returns to the browser as-is, such as CSS, JS, images and fonts. By contrast, most web pages in your application are dynamic: they read data from the database and display it. When Vapor receives a request for a path for which no route is configured, the FileMiddleware checks your Public/ folder to see if there is a static asset matching that path, and if so, returns it to the browser.

To confirm the browser is applying the styles in app.css to your page, let’s make a change to it. Open Public/styles/app.css and add the following line:

 body {
+    background: blue;
     font-family: sans-serif;
 }

Reload your page in the browser and notice that the background color of the page has changed to blue.

screenshot of web page with blue background

Also notice that you didn’t need to restart the server. With Vapor, changes to static assets and to template files show up as soon as you reload the page.

JavaScript

The auto-generated Vapor project doesn’t include any JavaScript, so let’s add some to get a feel for it.

Create a Public/scripts/ folder, then create an app.js file inside it. Add the following to the file:

alert("Hello, alert!");

Now we need to point our web page to this script file. In Resources/views/base.leaf, add a script tag just before your closing </body> tag pointing to app.js:

+<script src="/scripts/app.js"></script>
+
 </body>
 </html>

Reload your page and you should see a browser alert.

screenshot of web page with alert box

Placing script tags just before your closing </body> tag, as we did, ensures that the browser will display your page content before downloading scripts. This generally leads to a better user experience, because users can see your content as quickly as possible.

The default Vapor project uses base.leaf as the Leaf template for all the pages in the app. Because you referenced the CSS and JS files in this template, they’ll be applied to all pages in your app. If you only want one page to include a certain script (common) or stylesheet (less common), you can add them to a page-specific .leaf file instead of to base.leaf.

Third-Party Front-End Libraries

You don’t have to write all your CSS and JS yourself. Just as we use shared libraries for Vapor development, we can use shared libraries for front-end development as well. One of the most commonly used front-end libraries is Bootstrap, which provides useful default styles and JavaScript widgets.

The simplest way to get started with a third-party front-end library is to link to it on a Content Delivery Network (CDN), a network of third-party servers optimized for asset delivery. CDNs offer several advantages:

  • CDNs decrease the load on your web app server because the CDN handle requests for static assets.
  • CDNs can offer servers at many physical locations around the world, improving download speeds for nearby users.

Like many front-end libraries, Bootstrap provides instructions for how to link to it on a CDN. Add the following <link> tag into base.leaf before your existing <link> tag:

 <head>
     <title>#import("title")</title>
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
+        integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
+        crossorigin="anonymous">
     <link rel="stylesheet" href="/styles/app.css">
 </head>

Add these two new <script> tags as well:

 #import("content")

+<script src="https://code.jquery.com/jquery-3.2.1.min.js"
+    integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
+    crossorigin="anonymous"></script>
+<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
+    integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
+    crossorigin="anonymous"></script>
 <script src="/scripts/app.js"></script>

 </body>

Note that in addition to a Bootstrap script, we also added jQuery, a library Bootstrap requires to do its work.

To put Bootstrap to use, let’s add a tab control to our page. In Resources/Views/hello.leaf, replace the "content" area with the following:

 #export("content") {
-  <h1>Hello, #(name)!</h1>
+  <div>
+    <ul class="nav nav-tabs" role="tablist" id="myTabs">
+      <li role="presentation" class="active"><a href="#()tab1" aria-controls="tab1" role="tab" data-toggle="tab">Tab 1</a></li>
+      <li role="presentation"><a href="#()tab2" aria-controls="tab2" role="tab" data-toggle="tab">Tab 2</a></li>
+    </ul>
+
+    <div class="tab-content">
+      <div role="tabpanel" class="tab-pane active" id="tab1">
+        <h1>Hello, #(name)!</h1>
+      </div>
+      <div role="tabpanel" class="tab-pane" id="tab2">
+        <h1>This is tab 2.</h1>
+      </div>
+    </div>
+  </div>
 }

What’s up with the strange-looking “anchors” like #()tab1? When Leaf sees a #, it interprets it as a Leaf tag, and looks for a tag name like export or loop to give it a command to execute. We just want to output a # character to the HTML instead of calling a Leaf tag, and the way to do this is by using #().

Before we test out our page, let’s make things a little nicer by removing the alert we added in app.js, leaving it an empty file:

-alert("Hello, alert!");

Let’s also remove the blue background we added in app.css:

 body {
-    background: blue;
     font-family: sans-serif;
 }

Reload the page and you should see tabs you can click to switch the content. Bootstrap is working!

screenshot of web page with Bootstrap tab control

Hosting Assets Locally

When you’re considering using a CDN, it’s important to weigh the downsides along with the upsides. A CDN is a dependency on an external server, and if there’s a problem reaching it, your web page will appear, but the styles and scripts will be missing. CDNs can also make it more difficult to do development while you’re offline.

The alternative to using a CDN is to provide the third-party assets in your app itself, alongside your custom assets. Let’s give this a try.

Go to Bootstrap’s download page and click “Download Bootstrap”. Unzip the downloaded .zip file.

The Bootstrap download doesn’t include the jQuery script file Bootstrap needs, so we’ll need to download that separately. To be safe, we should check what version Bootstrap requires. An obscure reference on Bootstrap’s docs says we should check Bootstrap’s bower.json file, which in turn says we need a jQuery version in the range 1.9.1 to 3.x.x. Go to jQuery’s download page. At the time of this post, the latest version is 3.2.1, so it should be fine. (In the next part of this series, we’ll see how a package manager can automate this version-checking work.)

Click “Download the compressed, production jQuery 3.x.x” (whatever the version listed is). If the file opens in your browser, save it to disk. Rename the file to remove the version number; just call it jquery.min.js.

When storing third-party assets in your app, it’s conventional to put them under a vendor/ directory to make it obvious they’re from a third party. Let’s follow this convention by creating a Public/vendor/ folder, with bootstrap/ and jquery/ folders under it. Next, copy the following files into your app:

  • From the Bootstrap folder, copy css/bootstrap.min.css and js/bootstrap.min.js into Public/vendor/bootstrap/.
  • Copy jquery.min.js into Public/vendor/jquery/.

The rest of the files in the downloaded Bootstrap folder aren’t needed by our app, so we don’t need to copy them in.

Now update your <link> tag to point to the local version of the stylesheet:

 <head>
     <title>#import("title")</title>
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
-        integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
-        crossorigin="anonymous">
+    <link rel="stylesheet" href="/vendor/bootstrap/bootstrap.min.css">
     <link rel="stylesheet" href="/styles/app.css">
 </head>

Do the same with your <script> tags:

 #import("content")

-<script src="https://code.jquery.com/jquery-3.2.1.min.js"
-    integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
-    crossorigin="anonymous"></script>
-<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
-    integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
-    crossorigin="anonymous"></script>
+<script src="/vendor/jquery/jquery.min.js"></script>
+<script src="/vendor/bootstrap/bootstrap.min.js"></script>
 <script src="/scripts/app.js"></script>

 </body>

Note that we no longer need the integrity and crossorigin attributes. Those were necessary security measures when the assets were hosted on a separate server, but they aren’t needed for assets served by the same server as your page.

Run your page again, and the tabbed widget should still work. It’ll continue working even if you’re offline—the Vapor app includes everything it needs to run.

What’s Next?

In this post we dipped our toes into front-end assets by writing our own CSS and JS, linking to a third-party library on a CDN and including the library files into our app.

Downloading files yourself works fine, but it can be time-consuming and quickly becomes unmanageable when dependencies have their own dependencies. That’s not how we handle dependencies in Vapor itself: we use the Swift Package Manager to download them. In the next part of this series we’ll look at using npm, a package manager for JavaScript, to download our dependencies, as well as ways to decrease the download time for our assets by combining files.

If you want to learn more about CSS and JS, Big Nerd Ranch’s Front-End Essentials course is a week-long intensive study to get you up-to-speed quickly. Join us at the Ranch for one of our bootcamps, or we’ll come to you through our team training program.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project