Suspended together, his colourful, well-balanced mobiles are in harmony with themselves. All the discrete elements are carefully positioned to offset each other, and move and work cooperatively to be greater than the sum of their parts.
This is how a front end framework functions, it provides the structure onto which we can hang our code. By adding our own components, data services and business logic, we end up with a new and beautiful application.
Ultimately, a front end framework is a collection of libraries, tools and patterns that provide developers with a foundation on which they can build and develop complex web applications faster and more efficiently. While their processes might differ, they share some similar concepts and features.
Component-Based Encapsulation
The early days of the internet were a lot simpler. We had static, predominantly simple text and image-based web pages. Data was submitted with native form elements, (AJAX only arrived in 1999) so if you wanted to see the next page of data, you had to fetch the whole thing. For this reason, we thought of UIs in terms of pages.
With the introduction of AJAX, we were able to start breaking these pages apart into independently updating sections. Over time, and as more powerful browser APIs and more sophisticated frameworks started to arrive, a new method of creating UI evolved.
Component-Driven Development (CDD) turns the way of thinking about a page on its head, by allowing developers to create an interface from the bottom up. This modularization lends itself to code reuse and exemplifies the UNIX methodology of “do one thing and do it well”.
A result of the wide adoption of CDD is that all modern front end frameworks support some form of component creation. These components can be configured with attributes known as properties or “props”, and they can be composed together to make increasingly sophisticated interfaces.
Components across frameworks share a lot of similarities, but there can also be distinct differences. Some frameworks like Lit generate native web components, some frameworks like Angular give you a CLI (command-line interface) to generate the boilerplate for a new component, but they all differ in how you define your props, code, HTML and CSS.
Reactivity
The first time I was introduced to two-way data binding was through AngularJS. It felt like I had been taught some dark and powerful magic! With hardly any code, your interface could instantly update if some data changed, but also allow you to edit the same data and have it reflected everywhere in your app instantly! Doing this in native JavaScript would have been a big undertaking. It was the first taste of true reactivity, and frameworks have only got better at it since.
All frameworks have ways of generating HTML elements in any way the business logic demands, and re-rendering when needed. The way each framework renders the DOM (document object model) can wildly differ.
With React, JSX is the most popular way to define templates. It mixes JavaScript and HTML elements together, often iterated over using maps, and makes heavy use of ternary operators. React uses hooks and virtual DOM to trigger rendering updates.
Svelte, on the other hand, keeps the HTML and code separate in .svelte files which are then compiled down to native event handlers and callbacks doing the DOM manipulation - without a runtime.
Routing
A big part of the web experience is navigating between pages. We’re all familiar with the address bar and how it changes from page to page. The way a web application handles this navigation is called routing, and how it works can be very different depending on our goals.
Single page applications (SPAs) load a single application shell and emulate the traditional address bar changes and back / forward navigation. While in the background, the app is only loading the resources it needs to change the view and nothing more, giving the user a fast experience.
There are Server Side Rendering (SSR) and Static Site Generating (SSG) frameworks that use a file system-based approach to routing. They separate the code that runs during server rendering or build time, and the code that runs client-side in the browser. This results in web experiences that can be cached well, easily indexed, and quick to load. Once the first page is loaded, they can behave like a SPA, only loading what’s needed for the next page. Some are even able to preload the next view as soon as we hover over the button to navigate to it.
State Handling
With reactivity and data being sent all over the place, it’s not hard to imagine that if we don’t keep our state under control, bugs will be sure to follow.
Frameworks can provide the tools to reign in complexity by giving us different mechanisms to handle and distribute state. The common methods that frameworks use for this are:
Reactive stores - Objects that allow you to set and read data that trigger callbacks if the data changes.
Contextual distribution - This allows you to distribute state that’s only available to certain component hierarchies.
Prop setting - Allows for direct parent to child distribution of state.
Scoped state - State that’s only available to the individual components.
Tooling
The job of a framework is to make the developer’s life easier. To this end, frameworks often come with a whole suite of tools that attempt to reduce the repetitive work of creating applications. This tooling can include help with:
Bootstrapping
Test automation
Code splitting
Dependency management
Bundling
When is a framework not a framework?
… when it’s a library of course!
Frameworks and libraries are different, but the distinction sometimes confuses even seasoned programmers.
A library performs a specific function. For example, Redux is a state management library that is a collection of classes and methods dedicated to that one purpose. Your code instantiates and makes use of a library to perform a specific task.
However, when you use a framework, it calls your code. A framework runs the show. In the sculpture analogy, you can say your code ‘hangs off’ the framework. In software engineering, this is known as the inversion of control.
So when do the lines become blurry? React is an unopinionated library, it can stand on its own and do one thing and do it well - rendering and updating the DOM. In order to build complex apps, React is combined with other libraries and will often find itself in the centre of either a custom hand-rolled framework or an off-the-shelf one like Next.js. This is why you can’t ignore React when you discuss fully-formed frameworks like Angular or Vue, even if it is a humble library.
Conclusion
There is a lot of crossover in the problems that front end frameworks try to solve and the features that they provide. However, the patterns, the domain-specific language they use, and how opinionated they are all differ.
The purpose of front end frameworks is to allow developers to focus on writing business logic instead of reinventing the wheel and building from scratch every time. The structure and patterns provided result in predictability, which in turn leads to maintainable and scalable applications.
The running joke of the front end community is that a new framework is created every day (or maybe even by the time you’ve finished reading this article). In reality, I think we’re living in a golden age of frameworks where there are many mature and stable flavors to choose from. Each comes with its own strengths to suit your specific needs. So get out there and try some on for size.
In my next post, I’ll discuss how we navigated the onerous task of choosing our next framework and some surprising performance results!