Finding a Better Framework than Vue.js, React, and Angular

Note: You may be interested in this article's editorial introduction as well.

It’s the latter half of 2019, and front-end development continues to evolve at a breakneck pace. Vue.js, React, and Angular dominate the space, but even if there were a clear winner among them, it certainly wouldn’t be clear how long that might last.

For seasoned veterans and newcomers alike, even navigating the state of the mainstream options takes considerable time. Exploring alternatives can be a fun way to take a break from that while getting a glimpse of the future.

Whether or not these approaches stick around, the ideas within them are what will doubtless live on. For example:

  1. Tree-shakability: The ability to automatically include only the necessary portions of code before shipping to production. Already Angular and Vue.js are, to various extents, implementing this feature.
  2. Compile-time analysis: Shifting the burden of DOM update determination from a virtual DOM or other run-time approach to a build step. This method of encoding selective updates in JavaScript is the foundation of Angular’s next front-end renderer, Angular Ivy.

Ideas like these carry major advantages that will either skyrocket an alternative to the fore, or disrupt the mainstream into evolving their approaches in 2020. (Perhaps their impressive minimization of loading time, bundle size, and API surface will rub off, too.)

Even though they’ve been around for a few years already, you may have never heard of these technologies before—but tens of thousands of developers have, according to GitHub star counts. The reviews we provide below are meant to expose you to a small sample of new ideas, rather than to be a comprehensive guide.

We also didn’t cover all of the major alternatives—for example, Inferno would have been worth including here, and plenty of sites are using Riot for a reason. There are also stable projects like Ember and Meteor still in active use that simply don’t get as much press anymore. Our coverage of Web Components forerunner Polymer is currently on hold, since the Polymer Project recently had its own internal revolution in terms of its approach, addressing many of its original issues.

In any case, we encourage you to take any that interest you for a “test drive.” For readers who are especially short on time at the moment, our authors have summarized their views here:

Tech
(Since)
Claim to fame Biggest pain point How does it compare? The bottom line…
AMP
Aug 2015
Lightning-fast CDN-backed loads from Google SERPs Some people consider AMP to be detrimental to the web Some JS and CSS limits; most apps can still reach feature parity. A very convenient syntax and the only front-end framework that generates a “gray lightning bolt” on SERPs to indicate to users that the page will load instantly.
Svelte
Nov 2016
Fast, compiled, and reactive Introduces subtle language-level differences from JS Having one of the smaller footprints of the market makes it much faster than other frameworks. Learn it! The market will shift to Svelte’s ideas sooner or later.
Cycle.js
Nov 2014
Pure reactive, functional frontend apps with no boilerplate. Lacking in tooling and docs; automated testing is awkward A pleasure to work with and extremely expressive, but you’ll be writing almost everything from scratch due to the small ecosystem. Great for small projects, and worth keeping an eye on.
Mithril
Mar 2014
Tiny, fast, no-nonsense approach to single-page applications Smallish ecosystem (despite corporate use) Uses concepts familiar to React and Vue developers, but executes them better Relative lack of specialists aside, well worth it for greenfield projects and add-ons to legacy code alike
Hyperapp
Dec 2016
Compact, fast, pure and functional Lack of stateful components, enforced single state tree Simply different. Worth learning: Approach will help you learn great new patterns that can be useful with any framework.
Preact
Sep 2015
Small bundle size Lack of community support It’s basically React, but the developer experience is not as polished. Preact makes sense for projects where resource budgets are the main focus of development efforts (PWAs, embedded applications).
Aurelia
Dec 2014
Ease of learning, convention over configuration, standards compliance Small community: Not many resources in case of a problem, not many ready-to-use components Similar to Angular: a framework, but more lightweight, easier to learn, and faster to develop. Unlike React and Vue, no virtual DOM in Aurelia (nor Angular.) Like Angular, Aurelia leverages two-way binding and dependency injection. The Angular model, but made easy—worth using it wherever you are allowed to

In addition, every author put together a feature-equivalent demo, linked to at the top of each section. Happy exploring!


AMP Is Ready to Own Your Entire Front End

Companion demo: Repository | Live app

When I first started dabbling with AMP, I thought it was all about speed and little gray lightning bolts. Those struck me as completely worthwhile goals, but the rest of my team was skeptical: Isn’t AMP limiting? Doesn’t it block JavaScript? Can you even do CSS? What about our ads? Our branding? How do you wrangle a CMS into producing AMP-friendly output?

A Google search result showing the AMP-ready icon, a gray disc with a lightning bolt on it.

I didn’t know the answer to all of those questions. But the AMP demo looked better than most of our sites did, so I was optimistic that we could cross those bridges in due time. Given the payoff of insanely fast page loads for mobile users accessing our articles via SERPs, I felt it was worth at least a prototype.

It turns out I was right. The AMP version of our blogs looks similar to the non-AMP version; probably a bit better. JavaScript is allowed. So is CSS. So are ads, forms, analytics, performance monitoring—our AMP version wants for nothing.

On my next project, a thought occurred to me. If nobody minded, I’d just as soon build the whole site as one singular AMP version, rather than have the “normal” front end, paired with an AMP version. Recently the AMP team released a rather hilarious video that confirmed my suspicion: AMP can, and maybe should, power your entire front end.

I can’t say I’ve had the chance to implement that approach all the way through to a launch yet, but I intend to, and I’ll do so with the same confidence with which I approach any other project. Allow me to rebut some common objections to this.

Design

Most people are aware that AMP can render attractive combinations of HTML and CSS, but there are lingering concerns about certain CSS properties and values being unsupported. I found that this is simply not the case. Responsive web design is supported in full, as are web fonts, icon fonts, SVG, full-page background images—probably anything you’re used to using.

CSS

One of the first roadblocks I hit was when I attempted to load my normal front-end CSS into AMP: There’s an 85 kB limit on CSS and somehow over the years, our stylesheet had grown to about 270 kB. At first, I found this really discouraging, but it caused me to seriously consider the merits of utility-first CSS. Bootstrap and Tailwind are good examples of this, where you give an element a class of m-5 (or similar) instead of writing .thing { margin: 2em; } in your stylesheet. This approach allowed me to achieve design parity in only 42 kB, and I now believe it’s a better way to work, regardless.

JavaScript

It’s true that AMP does place some restrictions on the type of JavaScript you can run, and the sources from which you can load it. For this, I am thankful, as JavaScript dependencies tend to be the most fragile part of my projects. And actually, you can do a lot more with JS in AMP than I expected. AMP has helpful modules for common UI elements like show/hides, AJAX form submits, validation, instant content updates, and more.

There’s also amp-script, which allows you to execute nearly any arbitrary JavaScript, but scoped within the opening and closing <amp-script> tags.

State

It’s easy to assume that AMP is just for news sites: Clearly modern tools like data binding and JS state management are out of bounds… right? Wrong! AMP stands at the ready to bind DOM interactions with a global state object, thanks to amp-bind. In fact, the AMP project was the first platinum donor to the Preact library, suggesting that it intends to only increase feature parity with the leading JS frameworks.

Scope

Can AMP do everything that you could possibly do on a non-AMP site? No, of course not, by definition. But speaking more practically, it can deliver the 90% of the features you actually care about, which probably does you a favor by preventing your app from being an edge case of whatever framework it might otherwise use.

AMP as a Front-end Framework: A Surprisingly Effective Approach

It’s more abstraction than you normally prefer. It’s a bit less flexible. Guess what? Abstraction is not inherently bad. Flexibility is not inherently good. There is an appropriate level of each, and I believe AMP delivers it for most projects.

Plus, it delivers something that no other framework can: Google’s CDN, their SERP pre-fetching, and whatever SEO benefits they choose to convey beyond the attractive gray lightning bolt. A valuable ally for any site to gain!

Contributors

Scott Fennell
Scott Fennell

Freelance JavaScript Developer @ Toptal

Scott has written hundreds of WordPress themes and plugins. He specializes in third-party API integrations including Twitter, MailChimp, CloudFlare, Bitbucket, and Shopify. He's an experienced public speaker, having presented at WordCamp Portland and also the Google campus in Seattle. He is active as a technology writer, having been published on A List Apart and CSS-Tricks many times. Scott is also a former infantry officer in the US Army.

Like what you're reading?Get the latest updates first.

Thank you for subscribing! Check your inbox to confirm subscription. You’ll start receiving posts after you confirm.

Svelte: The JS Framework that “Disappears” at Compile Time

Companion demo: Repository | Live app

In my opinion, Svelte is the genuine “next big thing” in web frameworks, leading the way with some cutting-edge (r)evolutions in how to approach web development:

  1. Svelte is the first compile-time framework (Svelte committed this on Nov 15, 2016, and Ionic’s Stencil followed a few months later)
  2. Svelte is the first “language” that makes use of the destiny operator for truly reactive programing
  3. Svelte has also been a trailblazer in popularizing the idea that the virtual DOM is overkill

How Does It Feel to Work With Svelte?

I have to be honest, it feels weird at first, especially when you realize that it’s not JavaScript that you are writing, but something else. That’s until you find out that all the new concepts are there for a reason: to help you write more relevant code and avoid boilerplate.

I’m not a fan of template languages where you write code like this:

{#each cats as cat}
{/each}

…which is maybe why I like React. But here, Svelte seamlessly integrates with JavaScript’s understanding of async structures, allowing code like:

{#await promise}
  <p>...waiting</p>
{:then number}
  <p>The number is {number}</p>
{:catch error}
  <p style="color: red">{error.message}</p>
{/await}

This makes it much more interesting than template languages that only focus on logic structures.

The reactive part of Svelte—how it handles state management—is interesting too. The new destiny operator, $:, is technically compatible with native JavaScript label syntax but compiles to something altogether different. It’s tempting to use, but often the regular assignment operator is more appropriate:

<script>
  let value = 0;
  const click = () => {
    value += 1;
  }
</script>
<h1>Count {value}!</h1>
<button on:click={click}>
Click!
</button>

In the above case, there is no need to use the destiny operator since the assignment operator—triggered by an event—is overloaded with the re-rendering functionality.

<script>
  export let active;
  $: disabled = !active;
</script>
<button {disabled}>
Click!
</button>

But in this case, if you need to “react” to changes in your properties, you can’t use the assignment operator, since it will be executed only once. Therefore you have to use the destiny operator ($:) here.

If you think about it, it’s nothing else than an idiomatic version of React’s this.setState().

Documentation is awesome. svelte.dev really took the time to nail it: Everything you need is there, with great explanations and interactive examples.

Debugging is super easy thanks to source maps, but if you check the bundle.js Svelte generates, you’ll find a few hundred lines of legible JavaScript code which you could debug if you wanted to.

Is It Worth It?

If you’re not up-to-date with front-end development and are looking for where to get into it, Svelte is also a great choice. React is getting simpler and simpler, but there are many cases where it’s certainly overkill to create a new React app. Svelte takes important but intricate concepts, like state management and immutability, and hides them behind simpler ones, like reactive programming and the destiny operator. That makes it much faster and easier to learn even when you’re just starting out.

If you’re experienced in any other framework(s), you should definitely take a day or two to learn this marvelous framework. It will be useful for you, even just for quick prototyping, or for creating small apps.

It’s also certainly ready for large-scale apps as well. You don’t have to take my word for it: Svelte has been deployed in some serious real-world scenarios with blazing success.

Svelte: Everything You’ve Been Missing in Other Frameworks

It’s fast! Fast to learn. Fast to work on. Fast to run. I’d enjoy any engagement in Svelte, any time. I’m definitely using it for my next personal projects, and I strongly suggest that you do the same. React is cool, but it’s bloated with complicated computer science concepts to achieve the same goals. It’s time for something new and fresh, to start over and bring back the fun to web development.

Contributors

Alejandro Hernandez
Alejandro Hernandez

Freelance JavaScript Developer @ Toptal

Alejandro got his bachelor's degree in software engineering in 2005 and has since been working for software companies of all sizes from all around the globe as a freelancer. Currently, he enjoys working as a full-stack architect in JavaScript projects, where his experience and his deep understanding of architecture and theory are most impactful.

Like what you're reading?Get the latest updates first.

Thank you for subscribing! Check your inbox to confirm subscription. You’ll start receiving posts after you confirm.

Cycle.js: The JS Framework Leveraging Streams and Functional Programming

Companion demo: Repository | Live app

Before you can understand Cycle.js, you have to understand streams. You may have encountered them before if you do Node.js back-end development, or if you’re already familiar with one of the popular stream libraries: Most.js, RxJS, or Cycle.js’ own XStream.

For the uninitiated, it may be easiest to think of a stream as analogous to an array, except it spans time instead of space. Like JavaScript arrays, streams can contain just about anything, can be sparse, and may be heterogeneous.

Streams have a beginning and may have an end—or may continue on indefinitely. Their members exist in moments of time. The moment a new member occurs in a stream, it’s emitted via a callback function, much like a Promise or an Observable (both of which can be easily made into streams). They can be manipulated like arrays using functional programming mainstays: You can map, fold (analogous to reduce), combine (somewhat like concat), flatten, and filter them, among other things. Also, like many array functions, stream functions return a new stream of the transformed contents.

A Virtuous Cycle

By way of example—inspired by Cycle’s own tutorials—suppose you have a button and you want to count the number of times a user clicks it. You have already transformed the document’s click events into an event stream (we’ll skip over that part).

If it were an array of recorded click events, we might approach that by filtering for only clicks on the button. Supposing we also couldn’t just take the array length from there, we’d map them to 1s, and reduce those to a sum. Let’s see what that looks like in XStream:

// event$ is our stream of click events, and #clicker is our button
const count$ = event$
 	.filter(event => event.target.id === 'clicker')
 	.map(() => 1)
 	.fold((acc, cur) => acc + cur, 0)

// remember fold is reduce for streams
// it works the same except it produces a stream of the results over time instead of a single value

// event$ is a stream of click...click...click...click...click...
// count$ is a stream of 1...2...3...4...5...

You might subscribe to count$ with a function that updates a counter element in the DOM, or logs it to the console, or POSTs it to your back end.

That’s Pretty Neat, but What About Cycle.js?

Imagine you made everything in your front end a stream. You have streams of user input events, timer streams periodically triggering a refresh of live data, streams of fetch requests for retrieving data, streams of fetch responses, streams of application state, streams of DOM updates, and so on.

Now imagine at the heart of your app is a pure function that connects all those streams end to end. It takes input streams, called sources, transforms their members, and produces output streams, called sinks. Sinks are connected to modules that handle side effects. Some of those modules, called drivers, may provide their own output streams, which feed back into the pure function’s sources.

You could make your app work like this:

A flow chart showing a main cycle with some side-branches.  The main cycle consists of "DOM Driver" feeding a "'User input' stream" int "Merge and Map," which feeds a "'Fetch request' stream" into "HTTP Driver," which feeds a "'Fetch response' stream" into "State Driver," which feeds a "'State snapshot' stream" back into "DOM Driver." The browser DOM sends DOM events to, and receives DOM updates from, the DOM driver. A timer feeds a "'Periodic timer' stream' into "Merge and Map." "HTTP Driver" points to the internet. "State Driver" points to storage.

You will have created … wait for it … a Cycle(.js).

All the business logic in your app is essentially declarative, and lives in a terse but exceptionally readable core function which Cycle.js calls run. Or maybe a few of them: run functions can be nested and composed by linking their sources and sinks together.

All your functions can be pure except those that have side effects, like creating XMLHttpRequests or updating the DOM. All your modules are reactive by nature: They subscribe to streams and react to them, never concerned with managing outside state; inversion of control happens by default in Cycle.

Side effects live in their own silos away from your business logic, which is only concerned with how streams connect together. Cycle ships with a fine set of drivers for managing common side effects like asynchronous requests and DOM manipulation. It’s easy to make your own driver: They accept input streams—_sinks_, in this case—and may have output streams (sources), mirroring run. Whenever one of their input streams emits a new item, the driver is called.

The way you think about the flow of activity in your app maps directly to how your code is written; those conceptual flow charts you might draw while architecting an app are effectively pseudo-code with Cycle.js. Therein lies the framework’s real genius. If you think about this for a minute—or better yet, try it out—you’ll discover that you now have functional, reactive superpowers.

Superpowers? What’s the Catch?

Cycle.js proudly declares itself “mostly free of magic”—and it really doesn’t do much of the bad kind—but the tidy, expressive, predictable, and easily traceable functions at the core of a Cycle.js app are their own kind of thaumaturgy. If you think I’m over-selling it, give it a try or check out the extensively commented main function for my companion demo.

But sure, I hear you, and there are some caveats:

  • Cycle.js is a small fish in a big pond; it lacks a robust third-party module ecosystem and large community, and its documentation and tutorials are a little rough around the edges
  • It’s tough to introduce to a team, or even to use in a greenfield project that expects to add more developers in the future, for want of experienced developers
  • Cycle.js may be difficult to get your head around at first if you’re not familiar with functional programming concepts or just don’t like them
  • It doesn’t have much in the way of tooling, although there is a Chrome browser plugin
  • Writing unit and integration tests for Cycle.js is easier in some ways, but conceptually different: less mocking and isolating, but more asynchronicity

When Should I Use Cycle.js?

You can use it in production—I have, on several small commercial projects on which I was (and am) the sole developer—but it’s probably a bad fit for teams that aren’t into functional programming or are already happy with another framework.

If you’re intrigued despite the caveats, give Cycle.js a try in your next hobby or side project. Cycle.js is small, and it does play nicely with other frameworks, particularly React, so you can start enjoying Cycle.js’ benefits by building a small module for an existing app.

Contributors

Justen Robertson
Justen Robertson

Freelance JavaScript Developer @ Toptal

Justen is a full-stack JavaScript developer with over a decade of experience. He has worked on projects for some of the biggest brands in the publishing and recording industries and has worked directly with small businesses and nonprofits in many fields. His broad skillset enables him to cover all the web technology needs of small businesses and startups or to fill the gaps in larger teams.

Like what you're reading?Get the latest updates first.

Thank you for subscribing! Check your inbox to confirm subscription. You’ll start receiving posts after you confirm.

Hyperapp Brings the Best of the Elm Architecture into a Front-end Framework

Companion demo: Repository | Live app

Hyperapp is a super-minimalist UI framework. It’s highly declarative, borrowing the best parts of The Elm Architecture. Hyperapp has an extremely small footprint, weighing in at 4.3 kB minified or 1.9 kB minified+gzipped, which can be optimized further with tree-shaking. And yet, it’s quite feature-rich and allows you to write apps in a purely functional way.

Hyperapp brings a custom virtual DOM implementation and the recent from-the-ground-up v2 rewrite bakes the action “dispatch” layer right into the framework rendering layer. This results in some amazing conveniences. For example, built-in handlers can directly accept functions from “state” to “newly updated state”:

<button onclick={state => state + 1}>Increment</button>

Note: As of this writing, Hyperapp v2 is in beta, and its docs are sometimes incomplete. Not to worry: The Hyperapp team is constantly improving them, so it’s just a matter of time. You might also be interested in v1—many interesting projects have been built with it, after all.

Outstanding Features

  • Purely functional approach makes it extremely clear and easy to reason about dataflow.
  • Managed side-effects and effects-as-data approach à la Elm and Cycle.js—this is huge! If you use React, you’ll wish you had this too.
  • No app code boilerplate: It concentrates on what to do instead of how.
  • Highly testable: a consequence of your entire app being a pure function from state with effects isolated.

Biggest Drawback

While Hyperapp code is amazingly easy to reason about, there is one caveat: There’s no clear notion of a “component”. Hyperapp avoids being “yet another component-based UI framework”—cool in some ways, but using components as UI composition units would be handy for reusability, including its own local state and logic. (On the other hand, its functional composition is wonderful.)

Note that the Hyperapp team encourages developers to use Web Components’ Custom Element API to define native-like components - they’re the easiest way to run arbitrary code outside of the Hyperapp scope.

Show Me the Code!

Let’s look at the official “counter app” example. hyperapp brings in its own h function to assist in creating virtual DOM nodes, but with some Babel (or TypeScript) magic, it’s easy (and cleaner) to use JSX:

import { h, app } from 'hyperapp';

app({
 init: () => 0,
 view: state => (
   <div>
     <h1>{state}</h1>
     <button onclick={state => state - 1}>-</button>
     <button onclick={state => state + 1}>+</button>
   </div>
 ),
 node: document.getElementById('app')
});

Pure Functions

All the actions in Hyperapp are defined as pure functions that return either state or state with effects. This means all event handlers like onclick expect “reducers” to be passed, which will then update the state maintained by the framework. Reducers can be functions:

const reset = () => 0;
const decrement = state => state - 1;
const increment = state => state + 1;

app({
 init: reset,
 view: state => (
   <div>
     <h1>{state}</h1>
     <button onclick={reset}>Reset</button>
     <button onclick={decrement} disabled={state === 0}> - </button>
     <button onclick={increment}>+</button>
   </div>
 ),
 node: document.getElementById('app')
});

You can then move reducers to an external module, reuse them elsewhere, and test them in isolation!

Single Best Feature: Side Effects as Data

Effects are simple functions and are not included in the core of Hyperapp. The hyperapp-fx package contains a bunch of effects of all sorts and can be used in a Hyperapp app seamlessly today. In the near future, a whole bunch of official packages will be available, such as @hyperapp/time, @hyperapp/events and more. It’s also very easy (and fun!) to write your own.

One of Hyperapp’s most powerful patterns from The Elm Architecture is representing effects as data which the underlying framework can then handle. So, to perform some action with side effects—like making an HTTP call or writing to/reading from localStorage—the app doesn’t perform the effect. It instead passes the description of the effect to the Hyperapp framework. (This is like Cycle.js, where effects are handled by drivers rather than app code.)

Here’s an example of an HTTP call to get a random quote:

import { h, app } from 'hyperapp';
import { Http } from 'hyperapp-fx';

const getQuote = () => [
 '...', // the "loading quote" placeholder
 Http({
   url: 'https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1',
   action: (_, [{ content }]) => content
 })
];

app({
 init: 'Click here for quotes',
 view: quote => <h1 onclick={getQuote}>{quote}</h1>,
 node: document.getElementById('app')
});

The Http function here doesn’t make an actual HTTP call: It instead issues an instruction to the framework to do so, represented as pure data.

This is incredibly easy to test: Tests can assert against pure data being passed into the app and pure data describing the new state and effects to run.

Even things like setting focus are pure data in the application. For example, in the following snippet that sets the focus to an <input> element, the setFocus reducer is still all pure. The focus function from @hyperapp/dom only issues the “command” for the underlying framework to find an element with a certain id and perform the DOM operation:

import { h, app } from 'hyperapp';
import { focus } from '@hyperapp/dom';

const setFocus = (state, id) => [state, focus({ id })];

app({
 view: () => (
   <div>
     <input id="input" type="text" />
     <button onclick={[setFocus, 'input']}>Set Focus</button>
   </div>
 ),
 node: document.getElementById('app')
});

Also, notice a nice tuple syntax here for passing additional payload to action: onclick={[action, payload]}. Neat!

A Close Second: Subscriptions

Another magnificent Elm Architecture feature, Hyperapp’s subscriptions let the app listen for input from the outside world. The twist? Received events are—yet again—plain data on the app side. This is true even if it might have been generated by some impure code as the result of a side effect.

Subscriptions are also set up in a completely declarative way. Let’s say we want to update the app state’s time property with a new time value every second:

import { h, app } from 'hyperapp';
import { interval } from '@hyperapp/time';

const tick = (state, time) => ({ ...state, time });

app({
 subscriptions: state => [
   interval(tick, {
     delay: 1000
   })
 ]
});

How Does It Compare to Mainstream Frameworks?

I don’t think we need to compare them: Hyperapp implies a different approach, not necessarily better or worse than that of a React, Angular, or Vue.js. The point is that it does bring its own major benefits. Hyperapp is darn good to make yourself familiar with because both it and The Elm Architecture highlight so many elegant state management and effect-handling patterns. Declarative side-effects are, in particular, something I wish to become more prevalent in mainstream frameworks over time.

What Is Hyperapp Best For?

This is a generic framework for web interfaces and could be used instead of, e.g., React. It boils down to preference, really. The difference mostly lies in an imposed programming style, heavily favoring functional and declarative approaches. I wouldn’t consider it a “niche” one, either—it just works so well, and the density of useful patterns per LOC is super high. Definitely give it a try!

Contributors

Vasiliy Ruzanov
Vasiliy Ruzanov

Freelance JavaScript Developer @ Toptal

Vasiliy is an architect, multi-platform developer, hobbyist UI designer, and entrepreneur. He's an all-in-one performer and perfectionist in a great way. With more than eighteen years of experience in web programming and managing development teams, he's excited about the way the web is evolving and likes to be on the bleeding edge of modern technology.

Like what you're reading?Get the latest updates first.

Thank you for subscribing! Check your inbox to confirm subscription. You’ll start receiving posts after you confirm.

Preact: An Ultra-lean Drop-in Replacement for React

Companion demo (combined repository and live app)

Preact is a front-end framework that aims to provide the same feature set as React with only a fraction of its bundle size. But what differences does that approach bring? And how can you choose between them for your project?

Familiar API

On the surface, Preact components look identical to their React counterparts, together with lifecycle methods, JSX, and hooks. If you are trying to stay as close to React as possible, then you will not be disappointed with Preact.

Code Example

Here’s what a typical stateful component looks like:

import { h, render, Component } from 'preact';

class Hello extends Component {
  render() {
    return (
      <div className="greeter">
        <h1>Hello {this.props.name}</h1>
      </div>
    );
  }
}

render(<Hello name="world" />, document.body);

How Is Preact Different from React?

Features in the base Preact distribution go a long way towards developing a complete user experience. That being said, some differences may give you a moment of hesitation when moving from React to Preact. For example:

  • props.children is always an array, which may affect your implementation of the “render prop” pattern.
  • Portals are not available in the base distribution, but can be installed separately
  • Support for Fragments and the new Context API is missing (but planned)

How Does Preact Achieve a Smaller Bundle Size?

The Preact library weighs only 3.5 kB while providing equivalent functionality to react + react-dom which weigh 35 kB combined. Small bundle size is at the core of Preact design decisions, and achieving this goal would not be possible without making a compromise between performance and usability.

Preact vs. React Performance Comparison

The first thing that Preact does away with is virtual DOM diffing. Instead, Preact leverages browser functionality to a greater extent, achieving the same result in fewer abstractions. Benchmarks say Preact is slower in some cases and significantly faster in others. However, the negatives here seem to be negligible even at their worst.

Split Functionality and Tree-shakeable Modules

The Preact design philosophy also affects the way its code is organized. To make sure that only the minimum necessary code makes it into the final bundle, Preact extracts optional functionality, such as hooks, into separate modules. This enables developers to take greater advantage of tree shaking by sacrificing on code reuse in favor of self-contained functions.

Compatibility with React

In addition to providing the full complement of React features, Preact also makes it possible to use any components from the React ecosystem inside your Preact project. To do this, Preact provides a compatibility layer called preact-compat, which contains a full implementation of the public API found in react and react-dom, but using the functions provided by Preact.

preact-compat is the heaviest module that ships separately from Preact, adding an extra 4.1 kB to the bundle size.

To start using preact-compat you need to make it so that any external libraries which depend on react start using preact-compat instead. You can do this by changing your bundler configuration. Here’s an example for webpack:

{
    // ...
    resolve: {
        alias: {
            'react': 'preact-compat',
            'react-dom': 'preact-compat'
        }
    }
    // ...
}

You can also migrate an existing project to Preact using preact-compat with the exact same approach. This offers developers the flexibility to start using Preact at any stage of development instead of having to buy into Preact from the start.

But there are a plethora of libraries targeted specifically at Preact, i.e. they provide their own Preact bindings. So there is an ecosystem to tap into without needing preact-compat in a project.

When to Use Preact

When using Preact, occasionally, you might be the first to encounter an unforeseen edge case, and finding help will be harder. The popularity of React is one of the benefits that you would have to give up to replace it with Preact. However, there are definitely situations where Preact can be the most sensible choice.

Designing applications for the mobile web brings many extra challenges. One of the problems which aspiring developers of progressive web apps (PWAs) will face has to do with meeting performance budgets for critical-path resources. The small size of Preact plays very nicely in the context of the mobile web and can prove to be an effective tool in the hands of rational developers.

Embedded applications, too, can benefit from Preact’s conservative use of resources. React saw limited use in the context of <iframe> apps and web components because of its moderate size. Preact offers the only practical way to use the React API in this context.

Preact: Efficient and React-like, But Not a Default Choice

Preact offers developers a way to use the familiar React API in tight spaces where React itself wouldn’t be feasible. But developing with Preact is not without friction. You must carefully consider whether Preact is the right solution for your project.

Contributors

Michael Pontus
Michael Pontus

Freelance JavaScript Developer @ Toptal

Michael is a senior full-stack web developer, with over six years of professional experience working for leading software companies and overseas clients. He has a passion for building web and mobile applications and applying his knowledge to solve innovative engineering problems.

Like what you're reading?Get the latest updates first.

Thank you for subscribing! Check your inbox to confirm subscription. You’ll start receiving posts after you confirm.

Aurelia: The All-in-one Solution Angular Should Have Been

Companion demo: Repository | Live app

Aurelia is a collection of modern JavaScript modules, which when used together, function as a powerful platform for building browser, desktop and mobile applications, all open source and built on open web standards.

Aurelia's documentation

These include metadata, dependency injection, binding, templating, and router modules, which are all written in ECMAScript or TypeScript. Many of them modules can be used individually in any JavaScript project. While being a descendant of Angular.js (Angular 1), Aurelia differs from Angular 2+ by being unobtrusive, being more standards-compliant, and following the “convention over configuration” philosophy.

Aurelia Claim to Fame #1: Convention over Configuration

In line with this philosophy, Aurelia offers a set of defaults that enable developers to dive into development and business logic, rather than spending time with the initial configuration of components.

For example, in Angular, when you introduce a component, you have to specify the view accompanying the JavaScript or TypeScript file. However, in Aurelia, if both files have the same name, MyComponent.ts and MyComponent.html, Aurelia takes care of this configuration in the background for you.

You can always override the default conventions when needed.

Aurelia Claim to Fame #2: Gentle Learning Curve

I personally found it very easy to learn Aurelia. Having no experience with any JavaScript frameworks and libraries other than jQuery, it took me a couple days to understand the SPA concepts following the sample applications in the Aurelia website and the documentation provided. After a week, I was ready to write a production-ready network device management application for my client.

It would definitely have taken at least twice as much time if the framework chosen had been Angular or React. Aurelia’s standards-based, unobtrusive style, the skeleton projects provided, the extensive documentation, and the tooling—these get you going very quickly.

Aurelia Claim to Fame #3: Data Binding

Aurelia has three types of data binding available: one-way, two-way, and one-time.

Depending on the element, the bindings are applied automatically if not specified explicitly. For example:

<input type="text" value.bind="firstName">

Here, a two-way binding is created automatically for the input element. I personally find this very handy, where in other frameworks you have to spend a great deal of time repeating boilerplate to set up the bindings in form elements.

Biggest Pain Point

The only drawback of the Aurelia framework is the smaller community it has when compared to Angular, React, and Vue.js. I had some trouble finding solutions to my problems back in the early days of Aurelia. (Also, there were not many tutorials about Aurelia back then.) A related drawback is that there are many ready-to-use components for other libraries and frameworks where I had a hard time finding Aurelia components.

In the early days of Aurelia, there were not many third-party UI components available. For example, data grids with advanced features—I ended up implementing one for myself. Nowadays, they exist for Aurelia, but there will simply be less choices than with Angular.

Aurelia: A Worthy Alternative to Angular

I have developed projects in Angular, React, and Aurelia. I find Aurelia the easiest to learn and faster to develop when compared to the others. Conventions and standards compliance make life easy for the developer.

It’s mainly the client’s decision most of the time which library or framework should be used in a project. Having bigger communities and corporate backers, Angular and React are often the first choices—which is hard to argue, since there are not many Aurelia developers out there. Nevertheless, the simplicity and rapid development offered by Aurelia makes it definitely worth trying.

Contributors

Mustafa Süleyman Çiftçi
Mustafa Süleyman Çiftçi

Freelance JavaScript Developer @ Toptal

Süleyman is a full-stack software engineer with strong problem solving skills and educational background. He primarily works as a software architect on projects, but also develops front-end and back-end code depending on the compexity of the projects. He strives for efficiency, simplicity, and the best possible user experience. He is an extremely talented and versatile developer, and switching to a new platform is not a problem for him.

Like what you're reading?Get the latest updates first.

Thank you for subscribing! Check your inbox to confirm subscription. You’ll start receiving posts after you confirm.

Mithril: A Fast, No-nonsense JS Framework

Companion demo: Repository | Live app

With its recent 2.0 rewrite release, Mithril is an extremely fast and flexible single-page application framework used by the likes of Vimeo, Nike, Lichess, and Fitbit. Its API is tiny, making it easier to learn, but its feature coverage is just right for web apps, with routing and XHR supported out-of-the-box.

Mithril’s approach uses a virtual DOM and focuses on components, so in that sense, it’s somewhat similar to React. But having experimented with both React and Mithril around the same time, I can say that my experience building an app with Mithril was much, much smoother overall.

The Mithril Philosophy: “Less is More”

Mithril’s documentation is excellent and its learning curve is gentle enough. Its developers (and the official docs) focus primarily on real-life use cases.

Two features result from this:

  1. Mithril-based code generally runs blazingly fast by default, but it’s also easy enough to optimize when need be. This makes it especially suitable for embedded and mobile targets, but also for supporting the increasing number of desktops, laptops, and tablets running on (even yesterday’s) cheapest hardware.
  2. Mithril gets out of your way and lets you get work done. For example, when I built my first Mithril app, I found that component communication was an obvious use case rather than a puzzle to solve like it was with React.

Idiomatic yet Fairly Unopinionated

Mithril’s developers provide sensible idioms centered around Mithril’s polymorphic, variadic, and ubiquitous hyperscript function m().

They don’t insist on it, though. Mithril itself is as flexible as you need it to be. You can use JSX if you prefer it. But you may find that Mithril’s terse syntax is so easy to work with natively—carrying the side-benefit that you can use Mithril entirely without build systems like Webpack, like my companion demo does—it might end up convincing you there’s no need for JSX after all.

If you like CoffeeScript, the syntax becomes so minimal it approaches YAML territory:

ToggleButton  =
  view: (vnode) ->
    m '.toggle',
      m 'input[type=checkbox]', vnode.attrs
      m '.toggle-state'
      // ...

(Thanks to Davorin Šego for the above example.)

Mithril development also works just fine with TypeScript or even with Haxe, if you’ve heard of it.

Not everyone may like the React Hooks API, but if that’s what you’re used to, there’s a third-party Mithril library for that, too. Prefer streams? They’re included in an optional module.

There are also tools for converting plain HTML to Mithril hyperscript snippets, i.e. plain JavaScript using Mithril’s m()—but I bet any designer who can work with JSX directly can follow hyperscript syntax equally well.

Mithril Community? (Ever the Big Question for Alternative Tech)

Mithril has been around long enough that there is a small-but-dedicated following. You can find good help when you need it, via normal channels like StackOverflow, Gitter, etc.

Sure, like all the rest, a small community implies certain limitations when you’re looking for a quick add-on or trying to hire experienced developers. But that doesn’t stop Mithril from being awesome. I suspect that any developer well-versed in React would very quickly become genuinely productive if handed a preexisting Mithril codebase to maintain—or even if tasked with producing a greenfield one.

Remember, the developers of Mithril are tightly focused on real-world use cases. Mithril even has its own testing framework, ospec, which is a joy to use.

Mithril Gets Both Developer and User Experience Right

Mithril achieves some inspiring feats in size, performance, and developer experience, even as it continues to pragmatically evolve. Some may disagree with the design decisions behind it, as with any technology, but it’s still clear that these decisions were thoughtfully made.

The skill of coding with Mithril is transferrable enough, since you are still working with normal JavaScript, normal CSS, and normal DOM events—just more intelligently. Caution, though: Once you learn it, you’ll find it hard to go back to working with mainstream frameworks.

Contributors

Kevin Bloch
Kevin Bloch

Freelance JavaScript Developer @ Toptal

PostgreSQL, JavaScript, Perl, and Haxe are among Kevin's specialties, but he has worked with many programming technologies since his interest began during grade school. The bulk of his career has been as a lead desktop and full-stack developer, but his favorite areas of focus are project management, back-end technologies, and game development. He works diligently alone, but also loves being part of a team.

Submit a tip

Fields marked with an asterisk (*) are required

Thanks for submitting your tip proposal
Our editorial staff will review it shortly. Please note that tips proposals are subject to review and editing, and may or may not be selected for posting, at the sole discretion of Toptal, LLC.