If you’re looking to build a modern web application, a single page-application (SPA) may be a smart move. As users become familiar with SPAs via their social platforms (e.g., Facebook, Twitter) and productivity apps (e.g., Gmail, Trello), they begin to expect the same quick, smooth, and consistent experience from every web app they use—even internal enterprise ones. If you aren’t sure you need a single-page application, we cover some of the reasons and benefits in our previous blog post.
I’m not going to sugarcoat things: building a single-page application isn’t a particularly easy task. Technology in the front-end space moves and changes exceedingly fast, and as a result, there’s a ton of noise—primarily in the form of hundreds-of-thousands of npm packages. There are lots and lots of tiny decisions about what packages to use, what patterns to follow, client or server routing, etc. It can be easy to succumb to decision fatigue or decide to stick to the same old status quo, but I assure you: there is hope, and the effort is well worth the cost.
However, in order for you and your users to get the most value from a single-page application, it’s important to understand how to make effective choices in the front-end world. Here are some answers to the questions you’re sure to have as you embark on this wonderous journey.
What framework should I use?
Before you can start crafting your beautiful new single-page application, you need to decide what to build it with. The most popular options are React, Angular, and Vue. There are quite a few others out there, many with their own niche purposes, but these “big three” each have large, actively-growing ecosystems, and each are quite scalable. Now, there’s an important thing I want to call out here: These aren’t “all the same thing.”
For context: A library is something you leverage from your code, calling into and upon it as needed. A framework on the other hand, provides a base or “frame” for your application, and will call your code, exerting its own opinions on and about your code before producing an output.
- React, in-and-of itself, is only a view library (not a full framework) and it relies on other supporting libraries to build a full SPA. Many additional packages are provided or recommended by the React team to support it with clean integrations and maximum compatibility, but they are still separate libraries. This flexibility allows an incredible array of community-driven libraries to really shine and accelerate the ecosystem forward.
- Angular is a full framework with all the bells and whistles. You can opt to add more supporting libraries or third-party add-ons, but the core Angular packages have everything you need for many simple to medium-complexity SPAs. It’s been through a number of iterations and has become a solid, performant, battle-tested framework.
- Vue falls somewhere in the middle, marketing itself as a progressive framework: meaning you can include the pieces you need, as you need to (similar to Angular). The core library is only focused on the view layer, but with modern tooling and additional libraries, you can create large, complex SPAs (similar to React).
If your team already has a decent amount of experience using a particular SPA ecosystem, then use that one. It is often better to make use of and refine an existing skill set than to start over on a new learning curve if there’s not a good reason to change (such as needing the flexibility of React over the rigidity of Angular).
My personal favorite is React because it’s flexible, fast, and easy to read/understand. Oftentimes that level of flexibility is what allows me to move the fastest on any given project, picking and choosing what direction I want or need to take it.
Where should I start writing code?
Once you settle on a stack, it’s actually best to start with a command-line interface (CLI) tool that scaffolds a new app for you (e.g., create-react-app, angular-cli, vue-cli). These provide a quick way to start a new application by generating some basic pages with defaults included and packaging/bundling tools wired in. Using a scaffolded front-end project will save you a lot of time in getting the basic infrastructure in place and can even pre-wire some components for you (like client-side routing). Many of the CLI tools include helpful commands to generate new components, which can save time when working with boilerplate code.
If you’re building a back-end as well (and interested in using .NET), use the dotnet SDK SPA templates. They will go a step further and give you a prepared back-end, integrating the SPA and doing even more upfront configuration, and it’s as simple as installing the latest SDK, then typing `dotnet new react` from the command line. (Too easy, right?)
Once you have the foundation for your app, identify important features that have cross-cutting concerns or that will affect code throughout the application. For example, if your project needs to support localization then you need to be sure you design your front-end to accommodate the various languages. In these cases, it’s best to set up the tooling and patterns early on. Handling app-level concerns like this before getting deep into development will prevent the rework of threading them through the code later on and help ensure the patterns are established for any new developers.
Finally, as you start adding reusable code and shared components, be sure they are easy to discover. Consider including important notes about them in any onboarding documentation for the project. If ten different developers write a beautiful, reusable button each in their own way but don’t communicate it or make it easy to find, you may well end up with ten uglier, and slowly deviating buttons that are painful to replace later. The lesson here is that a little structure and governance goes a long way.
How do I pick additional libraries?
No matter what stack you’re building on, there’s going to be a number of additional libraries you’ll lean on to support your application. Choosing what to include in your SPA can either accelerate you or be detrimental to success. Here are some questions, guidelines, and food for thought for when you’re reaching for another library:
Do I already have something that does this?
As your app grows, so will the number of tools you have at your disposal. Before adding a new dependency, look at what you already have. Do a quick evaluation of shared code, and see if there’s already a component or helper function that serves your needs (this is really easy to do if you followed my advice earlier about making your shared code and components accessible).
If there’s nothing in your shared code, look at your `package.json` and scan it for anything that might include a workable solution (e.g., don’t add a multi-select component library if your existing select component has the ability to allow multi-select already — true story). Oftentimes there are features or packages that you may not have known or remembered were already available to you.
If you really don’t have anything, ask yourself…
Is there an official package for this?
By official, I mean something that was created specifically for your stack, by the makers of the stack or recommended by them. Angular includes a lot of features by default, but the Angular team has also published a number of additional packages that tightly integrate with the core framework better than third-party packages will. React on the other hand (being a library), points at or recommends a number of well-maintained community packages, like `react-router`.
Sticking to the official recommendations is usually the safest option. That said, there can’t be an official package for everything, which leads us to the next question…
How well is it maintained?
The vast majority of front-end packages are open source, and you can see everything from when the latest commits were made to what issues have been opened. There’s a lot of both subjective and objective input to consider, but there’s typically a clear indication if something is amiss, and you should be on the lookout for it before including the package on your project.
Taking a little time to look at the repo can tell you a lot about how well maintained a package is. If there hasn’t been a commit in three years, it might have been abandoned. Or maybe it has lots of recent commits, but they are all security-related fixes, and they state in their README that the project is going to be deprecated soon. These are both cases where you will want to seek alternative solutions.
How often is it released?
This is related to the last question, but can play a more specific role in keeping your project up-to-date. Most well-maintained projects will have a regular release cadence, or some level of roadmap that they follow to track what is going into the next version. Looking at how often a package is released can tell you how “stuck” you’ll be to the related set of dependencies. If you include a package that doesn’t get released often (even if it’s decently maintained), but other libraries you include have related dependencies, you may not be able to upgrade either of them until the next release. That’s one of the bigger tradeoffs when adding new packages.
Generally, this isn’t an issue if you stick to the most popular and official packages. In that scenario, your biggest challenge is keeping an eye out for breaking changes from release to release.
Have we used this before?
This is more for when you’ve pared down the number of options. If you have good internal lines of communication, it’s worth reaching out to other teams across the organization to see if has used the library/libraries in question. Someone who’s been there and used it can give you an opinion and some first-hand knowledge of any pitfalls they may have run into, or may rave about how amazing it is.
So, are there other tools to consider?
Yes… yes there are.
Consistency in a codebase is one of the best things a developer can have. Consistency helps keep code readable and thus understandable, which makes it easy to move from one part of the code to another during development. Luckily for us, there are plenty of tools that help. Formatting tools (like Prettier) help keep consistent styles throughout the code by automatically applying the configured styles as you save your changes, and linting tools (like ESLint) can help prevent other inconsistencies and even catch some small bugs using static analysis.
During development (especially on the front-end), you’ll have to rebuild your app to check on your changes. Hot-reload tools (e.g., react-hot-loader) allow you to see changes to running front-end code without reloading the app. This can speed up your workflow and make it quick to build out beautiful user interfaces.
Many of these types of development tools can take a little time to set up, but the long-term gains far outweigh the cost: from improved readability to early bug prevention.
Now go forth and build!
Building a SPA can be a big undertaking, especially if you’ve never done it before, but it’s not an impossible feat. In fact, you may have thought that a lot of topics seem very similar to concepts you’d apply to the backend… and you’d be right. While there’s a lot of new patterns and many things to be aware of, there’s a ton of overlap conceptually about how to make good development choices on both the back and front end of your project.
Starting with a CLI tool is definitely the recommended approach, and using one not only speeds you up, but ensures a lot of configuration is taken care of for you and isolated from inadvertent change. By combining this core stack with intelligent supporting libraries and taking care to make the development experience smooth, you can build some incredible user experiences with your SPAs.
Modern web applications have primed users to expect the latest and greatest…
…so why not give it to them, and have a little fun while you’re at it.