How to manage state in your app can often be the biggest and most impactful architectural decision you make.

Unfortunately, there is no standard practice for state management. Developers have to choose between a wide variety of techniques and libraries (many of them 3rd party), including Redux, MobX, state “tunneling,” singleton state services, or just hacking it together. Some of these solutions are optimized for large apps, and some for small ones.

With React Hooks, however, we finally have a state management technique that is both native to the framework, and a good fit for a huge swathe of apps (except, perhaps, very large ones).

If you aren’t familiar with Hooks in React, go read our introduction to Using React Hooks in Ionic React, it offers are primer on the new APIs and how to build basic apps with them. We will gloss over that in this post.

Let’s jump in.

State Management With React Hooks

React now ships with a number of hooks, including two that we can use to build a powerful state management system right into our app: useContext and useReducer.

At the risk of oversimplifying, a simple state management system has a few desirable properties: 1) it’s global, so state is managed in one place instead of all over your app and 2) individual components don’t modify or mutate state themselves, but rather emit “actions” to the state management system which can then mutate the state, causing the tree of components to update if necessary.

If you recognize redux in the above, congratulations! That’s effectively what we’re going to build with React Hooks.

The Pattern

Okay, let’s get to the pattern. We’re going to build our state management system in one file called State.jsx (or tsx if using TypeScript):

import React, { createContext, useReducer } from "react";

let AppContext = createContext();

const initialState = {
  count: 0
}

let reducer = (state, action) => {
  switch(action.type) {
    case "setCount": {
      return { ...state, count: action.user }
    }
  }
  return state;
};

function AppContextProvider(props) {
  const fullInitialState = {
    ...initialState,
  }

  let [state, dispatch] = useReducer(reducer, fullInitialState);
  let value = { state, dispatch };


  return (
    <AppContext.Provider value={value}>{props.children}</AppContext.Provider>
  );
}

let AppContextConsumer = AppContext.Consumer;

export { AppContext, AppContextProvider, AppContextConsumer };

In this file, we set up our Context, which our child components will access with the useContext hook. When they do this, they will have access to two things that we’ve set as the value on our AppContext.Provider: state and our dispatch function. Which are returned from calling the useReducer hook. state is the current global state, which can be used for rendering/etc., and dispatch allows components to emit actions that our reducer function will process to turn into a new state object.

The reducer function takes two arguments: the current state, and the action that was performed. It then returns a new state object that contains any differences after processing the action.

Let’s take a look at an example component to see how we’d use this:

import React, { useContext } from 'react';
import { IonButton } from '@ionic/react';
import { AppContext } from '../State';

export const MyComponent = () => {
  const { state, dispatch } = useContext(AppContext);

  return (
    <div>
      <IonButton onClick={() => dispatch({
        type: 'setCount',
        count: state.count + 1
      })}>
        Add to Order
      </IonButton>
      <h2>You have {state.count} in your cart</h2>
    </div>
  )
}

That’s pretty much it for the basic state management pattern! Our components access state from the Context and dispatch actions to the reducer, which in turn updates the global state, which causes components to re-render. Pretty simple!

There are a few other things we can add to our state management system to make it even more powerful, though.

Logging

A common need for state management is logging actions for debugging purposes.

Logging can be done very simply by wrapping the reducer function with a simple logging function and using that function as the argument to useReducer instead of the original reducer function:

const logger = (reducer) => {
  const reducerWithLogger = (state, action) => {
    console.log("%cPrevious State:", "color: #9E9E9E; font-weight: 700;", state);
    console.log("%cAction:", "color: #00A7F7; font-weight: 700;", action);
    console.log("%cNext State:", "color: #47B04B; font-weight: 700;", reducer(state,action));
    return reducer(state,action);
  };
  return reducerWithLogger;
}

const loggerReducer = logger(reducer);

function AppContextProvider(props) {
  // ...
  let [state, dispatch] = useReducer(loggerReducer, fullInitialState)
  // ...
}

Resulting in helpful log info like this:

Persistence

Another common need for a state management system is persistence, either of the entire state or a subset of it.

We can achieve this in a simple way using localStorage and adding a few lines of code to our state system:


const initialState = {...} const persistedState = JSON.parse(window.localStorage['persistedState']); function AppContextProvider(props) { const fullInitialState = { ...initialState, ...persistedState } // ... }

This first setups up the initial state to contain any data we’ve persisted in persistedState.

Then, to keep the persisted data up to date when state changes, we can use useEffect which will run every time our state is updated. In this example we’re persisting a new state.user field which might contain a user’s session token:

function AppContextProvider(props) {
  const fullInitialState = {
    ...initialState,
    ...persistedState
  }

  let [state, dispatch] = useReducer(loggerReducer, fullInitialState);

  useEffect(() => {
    // Persist any state we want to
    window.localStorage['persistedState'] = JSON.stringify({
      user: state.user
    });
  }, [state]);
  // ...
}

This will let us keep specific fields in our state persisted if they change, and load them back when the app starts up again. In that sense, the persistence is reactive and we don’t have to think about it. Note: using localStorage is bad for anything that needs to live for a long time as the browser/OS may clean it up. It’s perfectly fine for temporary data, however.

Conclusion

There you have it, a simple pattern for state management in Ionic React with React hooks. There are simpler state management patterns, to be sure, but I feel this strikes a nice balance between being simple enough for basic apps, and complex enough for decent sized ones, too. If I were going to build a Very Serious app, I’d probably still use Redux to benefit from the various libraries and techniques available there.

I like this pattern so much I’ve used it now on three different Ionic React apps. Much like a sourdough starter, I copy this state management system for each new app I build.

What do you think? Do you like this pattern? Could something be improved or tweaked? Let us know in the comments!

Read more

Today I am thrilled to announce the 1.0.0 release of Stencil—or what we’re calling “Stencil One.” 🎉

Last week at JS Conf EU, we had the pleasure of announcing the Stencil One final release on stage, just a few weeks after we released the beta. Now I’m excited to go in-depth and explain what this moment means for Stencil and Ionic.

Stencil’s creation is the direct result of years spent working on Ionic, developing a reusable UI component library for millions of developers and applications, and the many lessons learned along the way. So let’s dig into Stencil’s origin story, and where we’re going next.

First, The Problem ☝

The story of Stencil starts with a very specific problem. The Ionic team wanted to make it easy for anybody to use our component library, whether they preferred Angular, React, Vue, or some other framework. But at the time, Ionic components were only compatible with Angular.

In our quest for universal compatibility, it occurred to us that, as far as the framework logic is concerned, an <ion-button> element is no different than a <button>. Both are just elements in the DOM, and in both cases, the developer is able to interact with elements using properties, attributes, and events.

When we accept that <ion-button> is no different than any other element on the page, it immediately expands our universe of possibilities. The low-level framework actually doesn’t matter. In fact, without a dependency on any specific framework, using an Ionic component can be as simple as:

const btn = document.createElement('ion-button');
btn.textContext = 'Hello Ionic';
document.body.appendChild(btn);

This gave us enough to believe that Ionic could, in fact, decouple itself from unnecessary requirements, dependencies, and direct ties to a specific framework, or framework version — but not without some help, as you’ll see later.

Enter Web Components 🧩

The first step in our journey, then, was to move our component library to Web Components instead. More specifically, Custom Elements.

Web Components allow Ionic to use web-standard APIs already built into all modern web browsers, rather than framework-specific APIs that are version-restricted and may change over time. Essentially, this enables Ionic components to be created and connected to the DOM, no different than any other element.

The Second Problem 🤔

However, Web Components have purposely been designed as a very low-level web API, and as they scale and become more complex, things start to get a little trickier. Common scenarios include growing application complexity, team members moving on and off the project, business requirements changing, and creeping tech debt.

The complexity of maintaining apps can quickly escalate, which in my opinion is why frameworks such as React, Vue, and Angular have become so popular. They solve a real problem. Frameworks are the glue for components and are able to provide a common structure and best practices to help teams develop and maintain large applications.

So, while we saw the immense value of porting our components to Web Components in order to become framework agnostic, we immediately realized that in doing so we would lose out on all the great features and developer experience that come with frameworks. The questions quickly piled up:

How would we…

  • Setup a dev server that’s integrated with watching the file system?
  • Perform Hot Module Replacement or reload styles on file changes?
  • Prerender components in each framework?
  • Run unit and end-to-end tests?
  • Integrate these components in React, Vue, Angular, etc.?
  • Export component types for frameworks, JSX and HTML?
  • Document component properties, attributes, events, CSS variables, etc.?
  • Set up differential bundling for modern and legacy browsers?
  • Efficiently bundle modules using modern ESM standards?
  • Minify ES2017 builds and separately minify ES5 legacy builds?
  • Print error messages pointing directly to the source?
  • Precompile Sass and minify CSS?
  • Configure the package entry points for ESM, CommonJS, and CDNs?
  • Set up linting rules?
  • Take screenshots and perform image comparisons against the last commit?

The list above only scratches the surface problems that need to be solved without a framework. And by solve I mean, “figure this out entirely on our own”, because Web Components themselves do not provide any answers to the above questions. Each one is certainly doable, and there are many dependencies developers can piecemeal together.

However, the R&D time just to figure out how to answer all of the above questions with the wild west of external dependencies is a problem in itself. And not just to work on our development machines, but Ionic’s package setup must be capable of being imported by all bundlers, frameworks and external tools, which is something that’s not exactly cut-and-dried.

Enter Stencil 👋

We had to find a better way. And that’s what led us to Stencil.

Stencil is a compiler that generates standards-compliant Web Components, while also delivering the best concepts of the most popular frameworks into a simple build-time tool.

Stencil takes features such as

  • Async rendering
  • Reactive data-binding
  • TypeScript
  • JSX

and then generates standards-based Web Components with all these features baked in.

Since Stencil generates standards-compliant Web Components, we solved our original problem of making Ionic compatible with all popular frameworks right out of the box. They could even be used without a framework because they are just Web Components. And on top of that, Stencil also enabled a number of key capabilities, in particular, prerendering, and objects-as-properties (instead of just strings).

Compared to using Custom Elements directly, Stencil provides extra APIs that makes writing fast components simpler. APIs like JSX and async rendering make fast, powerful components easy to create, while still maintaining compatibility.

The developer experience is also tuned and comes with live reload and a small dev server baked into the compiler.

An Unexpected Benefit: Size & Speed ⚡

While our journey started with a desire to build a framework-agnostic component library, Stencil delivered some incredible – and somewhat unexpected – benefits. By making Stencil a build-time tool, and using the built-in capabilities of the browser via Web Components, we found that our bundle sizes for Stencil apps were incredibly small. Like, insanely small. And the speed of our components blew away our expectations.

Since Stencil is able to statically analyze the source of every component, the compiler is able to apply heavy optimizations and only include exactly what the components require. This means smaller file sizes, which are perfect for PWAs, and extremely fast startup times. Since every Stencil build will be different, below are commonly used examples in order to have a comparison.

Hello World: 83 bytes compressed, 133 bytes uncompressed
Todo MVC: 2.0KB compressed, 4.88KB uncompressed

And these same numbers scale quite well for all of Ionic’s components!

Design Systems 🧬

One of the reasons you’re even reading this blog is that we eventually made the decision to release Stencil as its own open source (MIT) project. Up until now, we’ve talked only about what Stencil has meant for the Ionic team, but the Stencil project has taken on a life of its own. In fact, we’re always excited when we hear from teams that are passionate about Stencil, and have no clue it’s made by Ionic.

Perhaps the most popular use case for Stencil is Design Systems. Design System initiatives have attempted to centralize brand, voice, componentry, and more for decades at large enterprises. However, when attempting to distribute actual code-based components for use in any project across the company, many enterprises have failed at gaining critical adoption due to having chosen one specific framework to make components in. Write your component library in React, and you’re only helping teams that use React in your company.

These teams have started to use Stencil to build their own custom component libraries, and have found success because the components they build will now work with any framework – helping to ensure their universal adoption within the organization – while also bringing the benefits of reduced bundle sizes and increased performance to their apps. Our team has since taken this a step further by developing specific solutions to address Design Systems directly. We call this offering StencilDS, and you can learn about it here.

What’s Next: Stencil Roadmap 🗺

Ionic has been built with Stencil since Ionic v4, so in a sense, Stencil has been in production for quite some time now. Before we released version 1.0 of the Stencil compiler though, there were a few areas of Ionic Framework’s development which helped discover more areas for improvement.

Because Stencil is a compiler that can do so many powerful things, surprisingly enough one of the biggest challenges is deciding what it should not do! Keeping the API tiny and continually aligned with web-standards is extremely important to us.

Some additional goals:

  • Stability: Continue to close out bugs while avoiding breaking changes at all costs.
  • Documentation: Continue to iterate on our docs by adding new content and focusing on writing clear and concise information.
  • Demos: Build more demos showcasing best practices we’ve discovered.
  • Prerendering and SSR: Stencil One can already prerender components, but the next step is to demonstrate how they can be used in other projects such as Angular Universal, Gatsby, and Next.js.
  • Community: We’re thrilled to have such a large, vibrant and helpful community already, but will keep working to further improve the ecosystem, become more transparent with our roadmap, and keep the community up to date via Twitter, Slack and soon a dedicated new blog on Stencil’s website.

Start Building! 🛠

Hopefully, this helped explain a little bit more of Stencil’s story, and where we’re going next. If you haven’t tried out Stencil yet, we encourage you to take five minutes and toy around with it:

npm init stencil

Follow our full Getting Started guide in the docs.

Thank You ❤

Lastly, on behalf of the whole Stencil team, I’d like to give a huge thanks to everyone in the community who has helped make Stencil One possible. Throughout this nearly two-year journey, your feedback, patience, contributions, and encouragement have helped us bring Stencil from an ambitious vision to an incredible reality.

We’re looking forward to hearing how Stencil One is working for you, and we’re excited to hear your thoughts, feedback, and questions.

Happy building! 🤙

Read more

Today I’m thrilled to announce the 1.0 release of Capacitor, Ionic’s new Native API Container that makes it easy to build web apps that run on iOS, Android, Electron, and on the web as Progressive Web Apps—with full access to native functionality on each platform.

Capacitor containerizes your Web App and provides consistent access to Native APIs for every platform that app runs on. That means, for example, that accessing the Camera uses the same code on iOS/Android as it does in Electron and on the web. This makes it easy to build one web app that runs natively on mobile, desktop, and the web!

Ionic apps use Capacitor much like Cordova to seamlessly run your app across a variety of platforms without platform-specific code. However, Capacitor brings some significant changes to the Cordova approach that you’ll want to read more about below.

With this release, we are one step closer to making Capacitor the official native container for every new Ionic app, so read on to see what this means and how it will affect your Ionic development:

How Capacitor works

Imagine a typical web app: You probably have some framework code to make your app render and to manage business logic. It’s likely written using a typical framework like Angular, React, Vue, or something like Stencil. Below that, you’re probably using a UI system like Ionic to get quality UI controls.

For many Web Apps, there’s not much more to think about because your app generally runs in a browser and, for good security reasons, you only have access to the set of Web APIs natively supported in the browser. However, that’s not true when running in a Web View environment on mobile and desktop!

In this case, because your app is running outside of the typical browser sandbox, there’s no reason your web app can’t access every Native API available on the given platform. For example, using biometric authentication and storing data in the keychain.

However, it’s not straightforward to take a Web App, put it in a native Web View control (as opposed to a full browser like Chrome/Firefox/Safari), and then proxy JavaScript calls to native code, returning data to your Web App in the process.

This is what Capacitor does: it containerizes your Web App and puts it into a managed native Web View (if running natively), then it exposes native functionality to your web app in a cross-platform way. Capacitor then provides a simple way to expose custom native functionality to your web app (through plugins), along with tooling to work with the web app container on the command line.

How is Capacitor different from Cordova?

In spirit, Capacitor and Cordova are very similar. Both manage a Web View and provide a structured way of exposing native functionality to your web code.

Both provide common core plugins out of the box for accessing services like Camera and the Filesystem. In fact, one of the design goals with Capacitor is to support Cordova plugins out of the box! While Capacitor doesn’t support every plugin (some are simply incompatible), it generally supports most plugins from the Cordova ecosystem.

However, at each step Capacitor has taken a slightly different approach that we think developers will enjoy, though some have tradeoffs to keep in mind:

Native Project as Source Artifact

Capacitor generally expects you to commit your native app project (Xcode, Android Studio, etc.) as a source artifact. This means it’s easy to add custom native code (for example, to integrate an SDK that requires modifying AppDelegate on iOS), build “plugins” to expose native functionality to your web app without having to actually build a standalone plugin, and also debug and manage your app in the way that embraces the best tooling for that platform.

We think this is the right balance since resources like Stack Overflow and typical native app project management work exactly the same as they do for native developers of that platform. This helps you take full advantage of help resources when you run into platform-specific issues, and enable blended teams that have native and web developers to work better together.

However, there are tradeoffs with this approach. Upgrades require more manual work as you have to bring along your native project. You can’t just blow away your native project and recreate it (unless you haven’t modified any of it). Installing plugins doesn’t modify your project so you will need to make manual modifications to preferences files like Info.plist.

However, we find that being able to manage your native project fully ends up being a big relief as your project scales.

No more deviceready

Capacitor kills the deviceready event by loading all plugin JavaScript before your page loads, making every API available immediately. Also unlike Cordova, plugin methods are exposed directly as opposed to being called through an exec() function.

That means no more wondering why your app isn’t working and why deviceready hasn’t fired.

Embracing NPM & Easier Plugin Development

Capacitor embraces NPM for every dependency in your project, including plugins and platforms. That means you never capacitor install plugin-x, you just npm install plugin-x and then when you sync your project Capacitor will detect and automatically link in any plugins you’ve installed.

With that in mind, making it easy to build and share plugins was one of the major areas of focus for us with Capacitor. Developers of Cordova plugins know that it is not easy to build plugins, and it’s nearly impossible to build and test them as standalone projects without importing into an existing app as they are generally just a collection of source files without project scaffolding.

Capacitor makes a few big changes to how Plugins work that also makes them easier to build. First, instead of copying plugin files directly into a project, Capacitor requires your plugins to be Cocoa Pods for iOS or typical android libraries for Android. Then, capacitor comes with a plugin:generate command to quickly scaffold out plugin projects, complete with Xcode and Android Studio projects and unit tests.

We hope this focus on improving the plugin development experience means more plugins will get built and plugins will be better maintained over time. If you’re interested in building your own plugin for Capacitor, take a look at our Plugin Development Guide.

Updated Plugin APIs

Developers will likely appreciate some of the changes we’ve made to core plugins, like the Filesystem plugin which is much more node-like and avoids the now-defunct Web Filesystem API.

First-class Electron and PWA support

Capacitor embraces Electron for desktop functionality, along with adding first-class support for web apps and Progressive Web Apps.

In fact, we’ve taken it a step further by building out a collection of UI experiences for APIs like Camera that bring the native app UI experience users expect to Progressive Web Apps. We call this the Progressive Web App OS.

On top of that, building plugins that offer web functionality as a fallback is just as easy as adding a few files to your plugin! Capacitor will only use your web implementation if the native one is not available, so users can use the exact same API when running on iOS, Android, Electron, and the Web.

Local CLI experience

Capacitor provides a tiny CLI that is installed locally to each app. That means there are no global dependencies to manage and it’s easy to use different versions of Capacitor across every app you build.

But that’s not all, folks!

There are several other changes we’ve made to the experience, and developers will probably notice that Capacitor “feels” quite a bit different than Cordova. To learn more, follow our installation guide.

What about Ionic Supporting Cordova?

Though Capacitor is meant to one day replace Cordova in each Ionic app, that doesn’t mean we’re no longer supporting Cordova! In fact, we’ve been building out a number of new plugins and features for Cordova, with the goal of also supporting them in Capacitor. We think Cordova is a great project that has been a key asset to Ionic’s success, which is why we’re in no rush to replace it and will be supporting it for many years to come!

That being said, we are obsessive about the developer experience behind building apps with Ionic, and see Capacitor as the way we can control every aspect of it and better align the native layer with the goals of Ionic, so we do plan to push Capacitor more prominently as time goes on.

Who’s using Capacitor?

Capacitor has been “out” for a little over a year. Since then, we’ve seen some pretty amazing apps being built on it, along with some major enterprise customers adopting it for apps that you will very likely use one day.

Top global fast-food chains are deploying in-store experiences for mobile and desktop on Capacitor. Companies like Southwest are building out all new hire onboarding experiences on Capacitor and Ionic, and popular consumer apps like Sworkit have built their latest-and-greatest versions on top of Capacitor (and Ionic 4 in this case!).

That’s just a small sample of teams building on Capacitor. Are you planning on building your next app with Capacitor? This email address is being protected from spambots. You need JavaScript enabled to view it. We’d love to learn more and even do a case study if you’re interested.

Getting Started

Yesterday we recorded a special Capacitor launch webinar. In it we demo’d some of the Capacitor APIs and showed off how simple it is to add Capacitor to an existing Ionic app currently using Cordova plugins. Didn’t make it? The recording can be found on our YouTube channel.

You can also get started right away. It’s easy to drop Capacitor into any existing modern web app (that includes, of course, Ionic apps).

Thank you

Capacitor has been in the works since late 2017 when we started to explore creating an alternative to Cordova. We were driven by the desire to make some significant changes to the approach pioneered by Cordova, as well as fulfill the need to fully support desktop and web platforms as Ionic’s mission expanded to helping teams build great apps everywhere.

Since then, we’ve had some wonderful contributions from the community and I want to thank everyone that filed issues, submitted PRs, or just spread the word about the project.

Now that Capacitor 1.0 is here, we hope you’ll see why we’ve invested in this new project over the last year and a half. We’re truly just getting started and expect to see a lot more Capacitor in the future.

Get started with Capacitor Today!

Read more

The latest version of the Ionic CLI has dropped, containing some great updates. To see the full list of changes, review the changelog. Read on for update instructions and the highlights.

NOTE: The Ionic CLI has a different versioning scheme than the Ionic Framework. Ionic Framework 5 doesn’t exist… yet. 🤓Run ionic info in your Ionic project for details.

Updating to Ionic CLI 5 🧗‍♀️

First, ensure that you have the latest NodeJS installed, which at this time is version 10. It’s easy to check which version you’re currently using:

$ node -v

To update the Ionic CLI to version 5.0:

$ npm install -g ionic

Next, install Cordova Resources (used to generate Cordova resources locally) and Native Run (used to deploy app binaries to devices):

$ npm install -g cordova-res native-run

For Angular projects, update to the latest @ionic/angular-toolkit:

cd my-ionic-app
npm install @ionic/angular-toolkit@latest

Notable Changes and Additions ☝🏻

native-run is now used to deploy app binaries to devices for ionic cordova run.

Native-run is a new tool we created to help ease the pain of deploying to simulators and real devices. Native-run is also platform independent, meaning it can work with both Cordova and Capacitor. Finally, it’s written entirely in JavaScript, so there’s no compilation step or platform specific code that could cause bugs on different platforms.

cordova-res is used to generate Cordova resources locally for ionic cordova resources.

Cordova-res takes the same great resource generation tools we had as part of our Ionic services, but moves them back onto the developer’s machine. This means you no longer need an Ionic account to generate splash screens and icons, resources can be generated offline, and since it’s local to the file system, you don’t have to wait for resources to download.

localhost is now the default host for ionic serve, ionic cordova run -l, and ionic capacitor run -l

--devapp is now a required flag if you want to use DevApp with ionic serve.

Now, something we’ve all been waiting for.

Ionic React Support (Beta) ⚛

The Ionic Framework team has been heads down on the Ionic React beta. A huge thanks to everyone in the Community who have submitted issues and provided feedback! It’s helping us shape version 1.0, which is right around the corner.

Some great Community articles have been published recently, too:
Creating a Medium clone with Ionic React
Build a news app with Ionic React and Axios
Ionic 4 and React: Navigation
Build a Chat App with Ionic React and Stream

Writing about Ionic React? Tweet us the link: @IonicFramework.

To use an Ionic React starter, ensure you have updated to the latest Ionic CLI (npm install ionic -g), then run:

$ ionic start myApp --type=react

Next, select a starter template, from a completely blank template to a complete conference example app:

Official documentation is coming soon. In the meantime, if you want to try Ionic React while it’s in beta, you should be comfortable with React and how things are done in a React app. Happy app building!

Read more

Today, we’re thrilled to announce the release of Stencil 1.0 beta (what we’re calling Stencil One), featuring an all-new compiler architecture. It is not only able to better optimize your components, but is designed to be completely future-proof.

What is Stencil?

If you’re not familiar with Stencil, it is an open source (MIT) toolchain that builds reusable, scalable Design Systems by combining the best features from popular frontend frameworks and generating standard Web Components (instead of a third-party component model).

Stencil was first announced in 2017 at Polymer Summit by my colleagues, Adam Bradley and Max Lynch. Originally called “Ionic core” internally, Stencil was born out of our desire to solve our own JS Framework dependency problems and increase performance for Ionic Framework. As it turns out, Stencil ended up solving a lot more than that.

Building Stencil allowed us to release Ionic 4 for Angular, React, Vue, or for no framework at all, powering roughly 100 cross-platform UI components in two separate styles (iOS and Material Design). As a result, the Ionic Framework team is now able to focus 100% of its time on shipping the best collection of UI components, gestures, animations, etc. without having to worry about browser-support, framework integrations, or performance—as Stencil automatically applies these built-in best practices.

Over the last two years, we’ve been surprised and incredibly humbled by the groundswell of community adoption Stencil has garnered. The project has over 200 contributors around the world, with thousands of developers, teams, and large organizations building on it. Frankly, we couldn’t be more grateful!

With that said, let’s dive into some of the exciting features shipping with Stencil One.

Machine Learning-inspired Bundling

Stencil One uses a new bundling algorithm inspired by “word embedding”, a popular Machine Learning technique. It allows us to pack components more efficiently, therefore reducing the network throughput and round-trips. In order to do this, we generate a N-dimensional vector space where each component represents a coordinate. These coordinates are generated based on the previous static analysis of how components depend on each other. Later, the components that are located close enough in the vectorial space are bundled together. A blog post explaining this technique will be coming soon!

While we bundle components to reduce the number of scripts, Stencil relies on ES modules that execute natively in the browser. Today, all major browsers (Chrome, Firefox, Safari and Edge) implement ES2017 and ES modules, which means we don’t ship any runtime to load modules.

At the same time, we still support old browsers such as IE11, thanks to “differential bundling”, which automatically generates the smallest possible bundle for each browser based on their capabilities.

In addition, all the metadata collected by the compiler and the dependency graph generated by rollup can be used to automatically create “modulepreload” links, reducing the amount of network round-trips required to download all the JS in the critical path to zero. This drastically reduces the Time-To-Interactive of apps built with Stencil.

Advanced Runtime Removal

With the new compiler, Stencil now shrinks the size and number of bundles sent to users. The metadata collected during the static analysis phase is used together with tree-shaking to completely remove the parts of the runtime that are not used.

While the hello-world demo is clearly an artificial example, we are excited about being able to generate incredibly small dependency-free components that include only what they need. Our 133bytes Hello World app (including runtime) is an example of the power of the compiler.

This optimization also scales as the complexity of the components increases. Here’s an example with the TodoMVC app:

JIT-friendly Runtime

We have also profiled and refactored the runtime to be smaller and much faster at execution-time. The new runtime avoids patterns that lead to non-optimal bytecode (usually called optimization killers), improves our async scheduler, and adopts new APIs like Constructable Stylesheets, native ES modules, and native async-await.

In this benchmark, we add 8,000 ion-button components (yes, eight-THOUSAND) to an app. At first glance, ion-button might appear simple, but it hides a great deal of complexity. It’s comprised of many classes and nested elements which give it the professional and polished visual appearance on both iOS and Android. Put another way, this stress test is attaching more than 200,000 nodes to the DOM.

Historically, it took ~6 seconds to fully initialize the app with well over 269,000 nodes:

With Stencil One, the exact same test finishes in 3 seconds. That’s 2x times faster! In addition, the memory usage is reduced from 180MB to 80MB.

Best of all, Ionic developers don’t have to go through any painful rewrites. Instead, they will automatically unlock these performance improvements on their existing projects just from the compiler upgrade.

A lot more in Stencil One…

The above is just a preview of some of the amazing features built into Stencil One. Here’s what else it will ship with:

  • New prerendering architecture compatible with Shadow DOM and designed to be integrated with third party tools, such as Angular Universal.
  • Generation of raw web components that extend directly from HTMLElement.
  • A new and powerful plugin system for third party output targets.
  • A continuing effort to ensure Stencil is as future-proof as possible.
  • A new compiler architecture that relies on ECMA features instead of Typescript for metadata collection.
  • Better built-in generation of Service Workers.
  • Better asset generation for more efficient cache policies.

We can’t wait to dive into all of these topics in upcoming blog posts!

Building a Design System?

We provide expert Advisory services & Software to large companies around the world building out the Design Systems that their company’s software will be built on for years and years to come.

If you or your company would like our help along the way, as well as other software surrounding Stencil and Design Systems, check out our Stencil DS product offering for building future-proof, production-ready Design Systems at scale.

Try the Beta!

We’d love for you to test out the Stencil One beta and to give us your early feedback. Upgrading your components to Stencil One is as easy as running the following command:

npm install @stencil/core@one

And remember to look at our breaking-changes file for any changes you might have to apply to your app.

We also released a new version of our Docs, documenting some of the new APIs of Stencil One.

What’s Next?

The Stencil One 1.0 release sets an important milestone in the development of Stencil, switching focus from being solely an open sourced internal tool, to an actual product that can help any developer or company building design systems or blazing fast apps using pure web components.

We are currently targeting early June 2019 for the final release. This is just the beginning for Stencil.

Read more

© 2019 Extly, CB - All rights reserved.