Web Front-end15 minute read

Ractive.js - Web Apps Made Easy

Ractive.js provides powerful capabilities for web app development in a way that is refreshingly simple to learn and use. In this article, Toptal Engineer Eugene Mirotin walks you through the process of building a simple Ractive search app, demonstrating some of Ractive’s key features and the ways in which it helps simplify web app development. Code samples are provided and explained.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

Ractive.js provides powerful capabilities for web app development in a way that is refreshingly simple to learn and use. In this article, Toptal Engineer Eugene Mirotin walks you through the process of building a simple Ractive search app, demonstrating some of Ractive’s key features and the ways in which it helps simplify web app development. Code samples are provided and explained.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Eugene Mirotin
Verified Expert in Engineering

Eugene is passionate about solving engineering problems. His language of choice is JavaScript, and the preferred stack is Node.js and React.

PREVIOUSLY AT

epam systems
Share

In today’s rapidly proliferating landscape of JavaScript frameworks and libraries, selecting the one on which you want to base your development can be quite a challenge. After all, once you go down the path of using a particular framework, migrating your code to using a different one is a non-trivial task which you may never have the time or budget to perform.

So, why Ractive.js?

Unlike other tools that generate inert HTML, Ractive transforms templates into blueprints for apps that are interactive by default. And while one could certainly argue that Ractive’s contribution is more evolutionary than revolutionary, its value is significant nonetheless.

What makes Ractive so useful is that it provides powerful capabilities, but does so in a way that is refreshingly simple for the developer to use. Moreover, it’s fairly elegant, fast, unobtrusive, and small.

In this article, we’ll walk through the process of building a simple Ractive search app, demonstrating some of Ractive’s key features and the ways in which it helps simplify web apps and development.

Ractive.js and web apps

What is Ractive.js?

Ractive was originally created to tackle the data binding problem in a more elegant way. Toward that end, it takes templates and transforms them into a lightweight virtual representation of the DOM so that, when the data changes, the real DOM is updated intelligently and efficiently.

But it soon became apparent that the approach and infrastructure employed by Ractive could be used to do other things more efficiently as well. It can, for example, automatically take care of things like reusing event handlers and automatically unbinding them when they’re no longer needed. Event delegation becomes unnecessary. As with data binding, this approach prevents the code from becoming unwieldy as an app grows.

Key features like two-way binding, animations, and SVG support are provided out-of-the-box, and custom functionality can be easily added via plugins.

Whereas some tools and frameworks force you to learn a new vocabulary and structure your app in a particular way, Ractive works for you, not the other way around. It also integrates well with other libraries.

Our Sample App

Our sample app will be used to search the Toptal database of developers based on skills. Our app will have two views:

  • Search: skills list with inline search box
  • Results: skill view including list of developers

For each developer, we will display their name, photo, short description, and a list of skills (each skill will link to the corresponding skill view).

(Note: Links to an online working instance of the application and the source code repository are both provided at the end of this article.)

In order to maintain our primary focus on the Ractive framework, we’ll employ a number of simplifications that normally should not be done in production:

  • Default theme. We will use Bootstrap with default theme for styling, rather than customizing the theme to fit your app style.
  • Dependencies. We will add our dependencies as separate scripts that define global variables (rather than using ES6 modules, or CommonJS, or AMD with proper loader for development, and the build step for production).
  • Static data. We will use static data I prepared by scraping the public pages on the Toptal site.
  • No client-side routing. This means that the URL will stay the same as we switch between views. You definitely shouldn’t do that for SPAs, though it may be OK for some small interactive components. Ractive doesn’t have a built-in router implementation, but it can be used with 3rd party routers, as shown in this example.
  • Templates defined inside script tags in HTML. This is not necessarily a bad idea, especially for small applications, and it has some advantages (it’s simple, and you can process these client-side templates together with your server-side templates, for example for internationalization). But for bigger apps you could benefit from pre-compiling (a.k.a., pre-parsing templates to internal JS representation.

Let’s Get Started with Web Apps

OK, so with that said, let’s start building the app. We are going to do it in an iterative manner, adding smaller features one-by-one and exploring the concepts as we encounter them.

Let’s begin by creating a folder with two files inside: index.html and script.js. Our app will be extremely simple and will work from the file:// protocol in order to avoid the need to start the development server (although you can if you wish).

The Search Page

We’ll start by implementing the search page where the user can select a skill for which to find matching developers in the Toptal database.

HTML Skeleton

Let’s start with this trivial HTML page:

<html>
    <head>
      <title>Toptal Search</title>
        <!-- LOAD BOOTSTRAP FROM THE CDN -->
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    </head>
    <body>
      <!-- SOME BASIC STATIC CONTENT -->
      <div class="container">
        <h1>Toptal Search</h1>
      </div>
  
      <!-- LOAD THE JAVASCRIPT LIBRARIES WE NEED -->
      <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.9.3/lodash.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/ractive/0.7.3/ractive.min.js"></script>
  
      <!-- LOAD THE DATA -->
      <script src="https://rawgit.com/emirotin/toptal-blog-ractive/master/data.js"></script>
  
      <!-- LOAD OUR SCRIPT -->
      <script src="script.js"></script>
    </body>
  </html>

As you can see, it’s a trivial HTML5 document. It loads Bootstrap from the CDN, Lodash (an awesome data-manipulation library), and Ractive.js.

Ractive does not require devoting the whole page to SPA, so we can have some static content. In our case, this consists of a container element and the page title.

Finally, we load the data I’ve prepared for the demo, and the script that will contain our program.

OK, now that our HTML skeleton is in place, let’s start adding some real functionality.

List of Skills

One of the things I particularly like about Ractive is how it teaches you to think about the final representation (HTML) you want to achieve, and then enables you to focus on writing the necessary bits of code to make that happen.

So first, let’s build a list of skills as our initial view. Doing this simply entails:

  • Adding an HTML element into which the skill list will be displayed.
  • Adding a small snippet of template code to our HTML.
  • Writing some brief and simple JavaScript that supplies the data to the template, to render it in the HTML element that we added.

The mods to the HTML consist of the following:

  <div class="container">
    <h1>Toptal Search</h1>
    <div id="root"></div> <!-- THIS IS THE NEW HTML ELEMENT -->
  </div>
  
  <!-- THIS IS THE SMALL SNIPPET OF TEMPLATE CODE -->
  <script type="text/html" id="tpl-app">
    <div class="row">
      {{#each skills}}
      <span class="col-xs-3">
        <a href="#" class="label label-primary">{{this}}</a>
      </span>
      {{/each}}
    </div>
  </script>

There’s no special convention with Ractive for specifying the HTML element to receive the data to be displayed, but the simplest way to do it is to add an ID to the element. I usually use the “root” ID for this purpose. We’ll see soon how it’s used when Ractive is initialized. For those curious there are other ways to specify the root element.

The slightly awkward script element with type="text/html" is a clever trick to get a chunk of HTML loaded by the browser without it being parsed or rendered, since browsers ignore scripts of unknown type. The content of the script is a Mustache/Handlebars-like template (though Ractive does have some extensions).

We first write the template code assuming that we have access to the skills array. We use the {{#each}} mustache directive to define iteration. Inside of the directive, the current element can be accessed as this. Again, we assume that the skills variable holds an array of strings, so we simply render it with an interpolation mustache {{this}}.

OK, that’s the HTML. But what about the JavaScript? That’s where the “magic” happens that supplies the data to the template::

  (function () {
    var skills = DB.skills, developers = DB.developers;
    var app = new Ractive({
      el: '#root',
      template: '#tpl-app',
      data: {
        skills: _.keys(DB.skills)
      }
    });
  }());

Impressive, no? In just that 10 lines of code we are able to:

  1. “Retrieve” the data from the “DB”.
  2. Instantiate a new Ractive app.
  3. Tell it to render inside of the element with id="root".
  4. Tell it to use a script element with id="tpl-app" to get the template (there are other ways of doing so as well).
  5. Pass initial data (we’ll see how to change it at runtime), or “scope” if you’re used to Angular terminology.
  6. Use the lodash keys method to get the skills names that we use as object keys in the “DB”.

So basically with this script we tell the framework what to do, but not how to do it. The template declares how, which I personally find pretty amazing and natural.

Hopefully you’re starting to get the idea, so now let’s implement something more useful on top of what we have.

The search page, of course, needs a search field. And we’d like it to provide an interactive capability whereby as the user types in the search box, the list of skills is filtered down only include those that contain the substring entered (with the filtering being case-insensitive).

As usual with Ractive, we start by defining the template (while thinking which new context variables will be needed, and what would change with regard to data management):

  <div class="container">
    <h1>Toptal Search</h1>
    <div id="root"></div>
  </div>
  <!-- THIS IS THE SMALL SNIPPET OF TEMPLATE CODE -->
  <script type="text/html" id="tpl-app">
    <!-- HERE’S OUR SEARCH BOX -->
    <div class="row">
      <form class="form-horizontal col-xs-6 col-xs-offset-6">
      <input type="search" class="form-control"
          value="{{ skillFilter }}"
          placeholder="Type part of the skill name here">
      </form>
    </div>
    <hr>
    <!-- NOW INSTEAD OF DISPLAYING ALL SKILLS, WE INVOKE A
         TO-BE-CREATED JAVASCRIPT skills() FUNCTION THAT WILL
         FILTER THE SKILL LIST DOWN TO THOSE THAT MATCH THE
         TEXT ENTERED BY THE USER -->
    <div class="row">
      {{#each skills()}}
      <span class="col-xs-3">
        <a href="#" class="label label-primary">{{this}}</a>
      </span>
      {{/each}}
    </div>
  </script>

Not so many changes, but still a fair amount to learn.

First, we’ve added a new <div> containing our search box. It’s pretty obvious we want to connect that input to some variable (unless you’re nostalgic about good old jQuery soup days). Ractive supports so-called two-way binding, which means that your JS code can retrieve a value without needing to manually read it from the DOM. In our case, this is accomplished by using the interpolation mustache value="{{ skillFilter }}". Ractive understands that we want to bind this variable to the value attribute of the input. So it watches the input for us and automatically updates the variable. Pretty neat with just 1 line of HTML.

Second, as explained in the comment in the code snippet above, now instead of displaying all skills, we will create a skills() JS function that will filter the skill list and only return those that match the text entered by the user:

  // store skill list in a variable outside of Ractive scope
  var skillNames = _.keys(DB.skills);
  
  var app = new Ractive({
    el: '#root',
    template: '#tpl-app',
    data: {
      // initializing the context variable is not strictly
      // required, but it is generally considered good practice
      skillFilter: null,
  
      // Define the skills() function right in our data object.
      // Function is available to our template where we call it.
      skills: function() {
        // Get the skillFilter variable from the Ractive instance
        // (available as ‘this’).
        // NOTE WELL: Our use of a getter here tells Ractive that
        // our function has a *dependency* on the skillFilter
        // value, so this is significant.
        var skillFilter = this.get('skillFilter');
        if (!skillFilter) {
          return skillNames;
        }
        skillFilter = new RegExp(_.escapeRegExp(skillFilter), 'i')
        return _.filter(skillNames, function(skill) {
          return skill.match(skillFilter);
        });
      }
    }
  });

While this is clean and simple to implement, you may be wondering about how it impacts performance. I mean, we’re going to call a function each time? Well, each time what? Ractive is clever enough to only re-render parts of the template (and call any functions from them) when their dependencies (variables) change (Ractive knows when that happens thanks to the use of setters).

Incidentally, for those interested in taking this a step further, there’s also a more elegant way of doing this using a computed property, but I’ll leave that for you to play with on your own if you like.

The Results Page

Now that we have a pretty usable searchable skill list, let’s move on to the result view where the matching list of developers will be displayed.

Switching to and from the Skill View

There are obviously multiple ways that this could be implemented. I selected the approach of there being two different views depending on whether or not a skill has been selected. If so, we show the list of matching developers; if not, we show the skill list and search box.

So, for starters, when the user selects (i.e., clicks on) a skill name, the skills list needs to be hidden and the skill name should be shown instead as the page heading. Conversely, on the selected skill view, there needs to be a way to close that view and get back to the list of skills.

Here’s our first step down this path:

  <script type="text/html" id="tpl-app">
    <!-- PARTIAL IS A NEW CONCEPT HERE -->
    {{#partial skillsList}}
      <div class="row">
        <form class="form-horizontal col-xs-6 col-xs-offset-6">
          <input type="search" class="form-control" value="{{ skillFilter }}"
                   placeholder="Type part of the skill name here">
        </form>
      </div>
      <hr>
      <div class="row">
        {{#each skills()}}
        <span class="col-xs-3">
          <!-- MAKE OUR SKILLS CLICKABLE, USING PROXY EVENTS -->
          <a href="#" class="label label-primary"
             on-click="select-skill:{{this}}">{{this}}</a>
        </span>
        {{/each}}
      </div>
    {{/partial}}
  
    {{#partial skillView}}
      <h2>
        <!-- DISPLAY SELECTED SKILL AS HEADING ON THE PAGE -->
        {{ currentSkill }}
        <!-- CLOSE BUTTON TAKES USER BACK TO SKILLS LIST -->
        <button type="button" class="close pull-right"
          on-click="deselect-skill">&times; CLOSE</button>
      </h2>
    {{/partial}}
  
    <!-- PARTIALS ARE NOT IN THE VIEW UNTIL WE EXPLICITLY
         INCLUDE THEM, SO INCLUDE THE PARTIAL RELEVANT TO THE
         CURRENT VIEW. -->
    {{#if currentSkill}}
      {{> skillView}}
    {{else}}
      {{> skillsList}}
    {{/if}}
  </script>

OK, there’s a LOT happening here.

First, to accommodate implementing this as two different views, I moved everything we had so far (i.e., the list view) to something called a partial. A partial is essentially a chunk of template code we are going to include in a different place (soon).

Then, we want to make our skills clickable, and when it’s clicked we want to navigate to the corresponding skill view. For this, we use something called proxy events, whereby we react to a physical event (on-click, the name is the one understandable by Ractive) and proxy it to the logical event (select-skill, the name is whatever we call it) passing the argument (as you probably remember this stands for the skill name here).

(FYI, an alternate syntax of method calls exists to accomplish the same thing.)

Next, we assume (again) that we’ll have a variable called currentSkill that will have the name of the selected skill (if any), or will be empty if no skills are selected. So we define another partial that shows the current skill name, and also has a “CLOSE” link that should deselect the skill.

For the JavaScript, the main addition is the code to subscribe to the select-skill and deselect-skill events, updating currentSkill (and skillFilter) accordingly:

  var app = new Ractive({
    el: '#root',
    template: '#tpl-app',
    data: {
      skillFilter: null,
      currentSkill: null,    // INITIALIZE currentSkill TO null
  
      // skills function remains unchanged
      skills: function() {
        var skillFilter = this.get('skillFilter');
        if (!skillFilter) {
          return skillNames;
        }
        skillFilter = new RegExp(_.escapeRegExp(skillFilter), 'i')
        return _.filter(skillNames, function(skill) {
          return skill.match(skillFilter);
        });
      }
    }
  });
  
  // SUBSCRIBE TO LOGICAL EVENT select-skill
  app.on('select-skill', function(event, skill) {
    this.set({
      // SET currentSkill TO THE SKILL SELECTED BY THE USER
      currentSkill: skill,
      // RESET THE SEARCH FILTER
      skillFilter: null
    });
  });
  
  // SUBSCRIBE TO LOGICAL EVENT deselect-skill
  app.on('deselect-skill', function(event) {
    this.set('currentSkill', null);    // CLEAR currentSkill
  });

Listing Developers for Each Skill

Having prepared the new view for the skill we can now add some meat — the actual list of developers we have for that list. For that, we expand the partial for this view as follows:

  {{#partial skillView}}
    <h2>
      {{ currentSkill }}
      <button type="button" class="close pull-right" 
              on-click="deselect-skill">&times; CLOSE</button>
    </h2>
  
    {{#each skillDevelopers(currentSkill)}}
      <div class="panel panel-default">
        <div class="panel-body">
          {{ this.name }}
        </div>
      </div>
    {{/each}}
  {{/partial}}

Hopefully by this point, you’re getting the feel for what’s going on here: we’ve added a new iteration section to our skillView partial that iterates over the result of a new skillDevelopers function that we’re going to write right next. For each developer in the array (returned by that skillDevelopers function), we render a panel and display the developer’s name. Note that I could use implicit form {{name}} here and Ractive would find the proper attribute by searching the context chain starting from the current context (which in our case is the developer object bound by {{#each}}), but I prefer being explicit. More information on contexts and references is available in the Ractive documentation.

And here’s the implementation of the skillDevelopers() function:

  skillDevelopers: function(skill) {
    // GET THE SKILL OBJECT FROM THE “DB”
    skill = skills[skill];
    // SAFETY CHECK, RETURN EARLY IN CASE OF UNKNOWN SKILL NAME
    if (!skill) {
      return;
    }
    // MAP THE DEVELOPER’S IDs (SLUGS) TO THE
    // ACTUAL DEVELOPER DETAIL OBJECTS
    return _.map(skill.developers, function(slug) {
      return developers[slug];
    });
  }

Expanding the Entry for Each Developer

Now that we have a working list of developers, it’s time to add more details, and a nice photo of course:

  {{#partial skillView}}
    <h2>
      {{ currentSkill }}
      <button type="button" class="close pull-right" 
              on-click="deselect-skill">&times; CLOSE</button>
    </h2>
  
    {{#each skillDevelopers(currentSkill)}}
      <div class="panel panel-default">
        <div class="panel-body media">
          <div class="media-left">
            <!-- ADD THE PHOTO -->
            <img class="media-object img-circle"
                 width="64" height="64"
                 src="{{ this.photo }}" alt="{{ this.name }}">
          </div>
          <div class="media-body">
            <!-- MAKE THE DEVELOPER’S NAME A HYPERLINK TO
                 THEIR PROFILE -->
            <a class="h4 media-heading" href="{{ this.url }}"
               target="_blank">
              {{ this.name }}</a>
            <!-- ADD MORE DETAILS (FROM THEIR PROFILE) -->
            <p>{{ this.desc }}</p>
          </div>
        </div>
      </div>
    {{/each}}
  {{/partial}}

Nothing new here from the Ractive side of things, but a bit heavier usage of Bootstrap features.

Displaying a Clickable List of Developer Skills

We’ve made good progress thus far, but one feature that’s still missing is the other side of the skill-developer relationship; namely, we want to display each developer’s skills and we want each of those skills to be a clickable link that takes us to the results view for that skill.

But wait… I’m sure we already have exactly the same thing in the skills list. Yes, in fact, we do. It’s in the clickable list of skills, but that’s coming from a different array than each developer’s set of skills. Yet these are sufficiently similar that it’s a clear sign to me that we should reuse that chunk of HTML. And toward that end, partials are our friend.

(Note: Ractive also has a more advanced approach for reusable chunks of a view, known as components. They are really quite useful, but for the sake of simplicity, we won’t discuss them now.)

So here’s how we accomplish this using partials (and note, incidentally, that we are able to add this functionality without adding a single line of JavaScript code!):

  <!-- MAKE THE CLICKABLE SKILL LINK INTO ITS OWN “skill” PARTIAL -->
  {{#partial skill}}
    <a href="#" class="label label-primary"
       on-click="select-skill:{{this}}">{{this}}</a>
  {{/partial}}
  
  {{#partial skillsList}}
    <div class="row">
      <form class="form-horizontal col-xs-6 col-xs-offset-6">
        <input type="search" class="form-control"
               value="{{ skillFilter }}"
               placeholder="Type part of the skill name here">
      </form>
    </div>
    <hr>
    <div class="row">
      {{#each skills()}}
        <!-- USE THE NEW “skill” PARTIAL -->
        <span class="col-xs-3">{{> skill}}</span>
      {{/each}}
    </div>
  {{/partial}}
  
  {{#partial skillView}}
    <h2>
      {{ currentSkill }}
      <button type="button" class="close pull-right"
              on-click="deselect-skill">&times; CLOSE</button>
    </h2>
  
    {{#each skillDevelopers(currentSkill)}}
      <div class="panel panel-default">
        <div class="panel-body media">
          <div class="media-left">
            <img class="media-object img-circle"
                 width="64" height="64"
                 src="{{ this.photo }}" alt="{{ this.name }}">
          </div>
          <div class="media-body">
            <a class="h4 media-heading" href="{{ this.url }}"
               target="_blank">{{ this.name }}</a>
            <p>{{ this.desc }}</p>
            <p>
              <!-- ITERATE OVER THE DEVELOPER’S SKILLS -->
              {{#each this.skills}}
                <!-- REUSE THE NEW “skill” PARTIAL TO DISPLAY EACH
                     DEVELOPER SKILL AS A CLICKABLE LINK -->
                {{> skill}}&nbsp;
              {{/each}}
            </p>
          </div>
        </div>
      </div>
    {{/each}}
  {{/partial}}

I already have the skills list attached to the developer’s object we retrieved from the “DB”, so I just changed the template a bit: I moved the line that renders the skill label to a partial and used this partial where that line was originally.

Then, when I iterate over the developer’s skills, I can reuse this same new partial to display each of those skills as a clickable link as well. Moreover, this also proxies the select-skill event if you remember, passing the same skill name. This means we can include this anywhere (having the proper context) and we will get a clickable label that leads to the skill view!

Final Touch — Preloader

OK, we now have a basic functional app. It’s clean and works fast, but it still takes some time to load. (In our case, this is partially due to us using non-concatenated, non-minified sources, but in a real world app there can be other significant reasons, such as the loading of initial data).

So as one final step, I’ll show you a neat trick to add a preloading animation that will be removed as soon as Ractive renders our app:

  <div class="container">
    <h1>Toptal Search</h1>
    <div id="root">
      <div class="progress">
        <div class="progress-bar progress-bar-striped active"
             style="width: 100%">
          Loading...
        </div>
      </div>
    </div>
  </div>

So what’s the magic here? It’s actually quite simple. I added some content (it’s a Bootstrap animated progress bar, but it could be an animated GIF, or whatever) straight to our root element. I think it’s pretty clever — while our scripts are loading, the user sees the loading indicator (since it doesn’t have any dependency on the JavaScript, it can be displayed immediately). However, as soon as the Ractive app is initialized, Ractive will overwrite the content of the root element (and thereby erase the preloading animation) with the rendered template. That way, we are able to achieve this effect just a piece of static HTML and 0 lines of logic. I think that’s pretty cool.

Conclusion

Think about what we’ve accomplished here and how easily we’ve accomplished it. We have a pretty comprehensive app: it shows a list of skills, allows searching through them quickly (and even supports interactive updating of the skills list as the user types in the search box), allows navigating to a particular skill and back, and lists the developers for each selected skill. Furthermore, we can click on any skill listed by any developer to take us to the list of developers with that skill. And all that with less than 80 lines of HTML and less than 40 lines of JavaScript. To my mind, that’s quite impressive and speaks volumes about the power, elegance, and simplicity of Ractive.

A working version of the app is available online here and the full source code is public and available here.

Of course, we’ve just barely scratched the surface in this article of what’s possible with the Ractive framework. If you like what you’ve seen so far, I’d highly encourage you to get started with Ractive’s 60 second setup and begin exploring for yourself all that Ractive has to offer.

Hire a Toptal expert on this topic.
Hire Now
Eugene Mirotin

Eugene Mirotin

Verified Expert in Engineering

Tallinn, Estonia

Member since March 31, 2015

About the author

Eugene is passionate about solving engineering problems. His language of choice is JavaScript, and the preferred stack is Node.js and React.

authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

PREVIOUSLY AT

epam systems

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

Join the Toptal® community.