Quick look at Preact

Jacek Mazgaj

As the growing advancement of web technologies, client web applications aim to provide user experience to as close as possible to the native applications (see the PWA term). 

This trend makes web apps overloaded with tons of javascript code to cover dynamic requests and UI changes while still trying to respond to user activity in real time. The performance of user devices grows, making them able to handle more processing, but another problem might be slow internet connection causing the load of javascript bundles to take lots of time.

Another struggle is on the side of developers. Writing complex and rich web applications in pure javascript can lead to panic attacks, heart strokes, mental illnesses or at least head aches. The cure for those problems was the rise of modern javascript web frameworks.

Switching to any of JS frameworks reduces the need of writing lots of code in comparison to pure JS, but in same time can produce other issues. The „simplest” of them is the need to learn the conventions and components of a framework. The other issue could be the need to learn new programming language (which still claims to be based on JS).
By the end of 2019, we can juggle with almost dozens of such frameworks, where every one of them claims to be „the cure for all your problems”. And here’s another.

So what is Preact?

preact – a verb, meaning „to act or perform beforehand”.

Some malcontents could mumble „it’s just another JS framework, nothing special”. Only the smallest part of that sentence might be considered as truth, because there are no components, technologies or techniques to write fully automated business dashboards with remote data, caching and all the fireworks within few lines of code.

But Preact is still more than worth to take a look at. Their homepage hits the eyes with a short slogan:

Fast 3kB alternative to React with the same modern API.

The name similarity and the slogan just makes it obvious that we are dealing with something being very close to React and yes, the React components can be reused in Preact, therefore the switch to Preact will be very smooth for current React developers. Preact comes with lots of features which could be found in React like: virtual DOM, ES6 classes, functional components, JSX, React DevTools, HMR, SSR.

The size of minified and gzipped size of Preact isn’t baloney.. Even the size reduction of React in its latest releases still keeps Preact on the first place on the podium.

Preact is developed under MIT license making it open-source and available for commercial use. The initial releases took place in mid-2015, probably making Preact one of the youngest in JS web framework family, therefore its community isn’t so big. The amount of weekly downloads number of react package through npm blows other frameworks out of the water.
But wait, if React components can be reused in Preact, then it ends up as community(Preact) += community(React), isn’t it?

Performance

Besides having the smallest size, Preact is probably one of the fastest JS frameworks.

You can run this benchmark tests by yourself here: https://developit.github.io/preact-perf/.

Note: It uses very old version of Preact (current one is 10) and results my vary dependable on resources availability on used device.

Installation

The recommended way of creating Preact apps is to use the dedicated preact-cli.
First, make sure to have the newest version of nodejs and npm installed, then install the CLI with:

npm i -g preact-cli


Now the preact command can be executed in terminal. Running it without any arguments shows commands and their basic usage.
To see the list of options of available commands use preact <command></command> -h:

preact create -h


It will also show which options are enabled by default. To switch on an option just pass it as next argument, but the switch off needs to be explicitly stated as --install false.

For more information see the preact-cli repository README file. It also covers the customised configuration of preact-cli internals with preact.config.js file.

Creating skeleton app

The basic command syntax to generate Preact application is preact create <template> <destination>, for example:

preact create default my-preact-app

With given arguments, this will generate the my-preact-app app under the directory with the same name and install the dependencies. By default create uses npm, to replace it with yarn add the --yarn option to create cmd. To see the list of official templates run preact list.
If you want to use custom template take a look at tip under Official Templates section.

NOTE: some of the official templates (e.g. material and simple) are using outdated Preact versions and does not include some dependencies which may result in errors when importing React components.

Preact apps comes with the routing dependency (preact-router) and embedded compatibility layer between React as preact/compat, which contains components such as React or ReactDOM.
Similarly to React, the package.json.gitignore and other files are being generated, but the advantage of creating apps with templates adds some starter code, which can be useful for newcomers (for example netlify template adds whole CMS).

The initial structure of src folder generated with default template is:

src
|   index.js
|   manifest.json
+---assets
|   |   favicon.ico
|   \---icons //omitted the icon list to maintain readability
+---components
|   |   app.js
|   \---header
|           index.js
|           style.css
+---routes
|   +---home
|   |       index.js
|   |       style.css
|   \---profile
|           index.js
|           style.css
\---style
        index.css

The routing based on url changes is done in app.js, it’s possible to use optional url parameters using ? after parameter name. To handle requests of non existing paths (aka 404) put default in the end of route declaration:

<home path="/"> 
<mycmp path="/showCmp/:optional?">
<div default="">404! Page not found!</div>

For more info on routing capabilities, take a look at readme on preact-router repository.

Custom components

Preact allows the developer to create a set of different types of components such as functional, class and fragments. The lifecycle methods of components are almost identical to the ones shipped with React. Below is an example of custom component:

import { Component } from 'preact';
import style from '../home/style.css';
import React from 'preact/compat';
import { useState } from 'preact/hooks';

export default class Github extends Component {
    state = {
        user: '',
        propUser: '',
        repositories: null,
        isLoading: false
    };
    loadGitData = user => {
        this.setState({ isLoading: true, repositories: null });
        fetch(`https://api.github.com/users/${user}`)
            .then(response => {
                if (response.status === 404) throw new Error();
                return response.json();
            })
            .then(user => {
                this.setState({ repositories: user.public_repos + '', isLoading: false });
            })
            .catch(() => this.setState({ repositories: 'err', isLoading: false }));
    };
    fetchUser = e => {
        e.preventDefault();
        this.loadGitData(this.state.user);
    };
    setUser = e => {
        this.setState({ user: e.target.value, repositories: null });
    };
    static getDerivedStateFromProps(nextProps, prevState) {
        if (nextProps.user !== prevState.propUser) {
            return {
                user: nextProps.user,
                propUser: nextProps.user,
                isLoading: false,
                repositories: null
            };
        }
        return null;
    }
    componentDidMount() {
        if (this.state.propUser) {
            this.loadGitData(this.state.propUser);
        }
    }
    componentDidUpdate(prevProps, prevState) {
        if (this.state.propUser && this.state.propUser !== prevState.propUser) {
            this.loadGitData(this.state.user);
        }
    }
    render({}, { user, repositories, isLoading }) {
        const repoText = <span> has {repositories} repositories on Github.</span>;
        const errText = <span> was not found!</span>;
        const [on, setOn] = useState(false);
        function toggle() { setOn(!on); }
        return (
            <div class="{style.home}">
                <h1>Enter a Github user name</h1>
                <form onsubmit="{this.fetchUser}">
                    <input tabindex="{0}" type="text" value="{user}" onchange="{this.setUser}">
                    <button type="submit">Submit</button>
                </form>
                { !isLoading && !repositories && <h5>waiting for submit</h5> }
                { isLoading && <h5>...fetching data...</h5> }
                { repositories &&
                    <h3>The user <span style="{{" fontsize:="" 'larger'="" }}="">{user}</span>
                        { repositories !== 'err' ?
                            repoText
                            : errText
                        }
                    </h3>
                }
                <button onclick="{toggle}">Switch1!</button> - {on ? 'Is on!' : 'Is off!'} <br>
            </div>
        );
    }
}

This component covers the basic use case of working with state and properties, fetch method, a few of component lifecycle methods, basic of hooks (the useState method), conditional rendering and usage of conditional parameters (only if you will create the route to this component). To see it working, after you generated the skeleton app, paste the code to the src/routes/github/index.js and after amend the app.js with:

<router>
    <github path="/github/:user?">
</github></router>

All of URL parameters are passed to the component props object. If you dont want to use conditional parameter, you can pass the default value similarly to:

<github path="/github/" user="proob">
<github path="/github/:user">

Not using the optional parameter and not declaring the default value for it, will result in 404 when requesting the /github path.

Others

To change the contents of index.html generated while building, modify or add the src/template.html with content similar to:

    <meta charset="utf-8">
    <title><% preact.title %></title>
    <meta name="description" content="Preact sample application">
    <meta name="author" content="Jaco Gaco">
    <meta name="application-name" content="Preact PWA Sample">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <% preact.headEnd %>


<% preact.bodyEnd %>

Preact is PWA ready, the app.js provides the shell and running build generates default service worker.
To use your own implementation of service worker, place it in src/sw.js file.

Preact has a well structured guide which explains most of the aspects and features and has a section covering the switch from React.
The description of core preact repository contains lists of demos, starter projects and useful libraries.

Afterthoughts

The possibility of using templates when generating new applications can really boost up the development by using configuration and components provided by the template. Being one of the fastest and smallest in size, it gives the guarantee of high performance. Learning curve for React developers is minimal and changing the React app to Preact requires very little configuration changes. The huge base of React components can be used in Preact applications without any worries.
Preact by default creates the project with PWA features and taking previous factors into consideration, Preact turns out as a great choice for creating modern, web applications.

  1. Preact official site
  2. Github gist with sizes of JS frameworks
  3. Online benchmark tool for JS frameworks
  4. preact-cli repository
  5. Official Preact pemplates
  6. preact-router repository repository
  7. Official Preact guide
  8. Core Preact repository with lists of demos, starter projects and useful libraries.
  9. Repository with a sample Preact app

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami