Skip to content

This Dot Blog

This Dot provides teams with technical leaders who bring deep knowledge of the web platform. We help teams set new standards, and deliver results predictably.

Newest First
Tags: Angular
QR Code Scanning & Generation cover image

QR Code Scanning & Generation

QR codes provide a very valuable way of sharing information with users, and many applications rely on them for various purposes....

Angular 17: Continuing the Renaissance cover image

Angular 17: Continuing the Renaissance

Dive into the Angular Renaissance with Angular 17, emphasizing standalone components, enhanced control flow syntax, and a new lazy-loading paradigm. Discover server-side rendering improvements, hydration stability, and support for view transitions....

You Don't Need NgRx To Write a Good Angular App cover image

You Don't Need NgRx To Write a Good Angular App

NgRx is a great tool that allows you to manage state and side effects in Angular applications in a Redux-like manner. It streamlines state changes with its unidirectional data flow, and offers a structured approach to handling data and side effects. Numerous posts on our blog detail its strengths and affiliated techniques. Some Angular developers even argue that incorporating NgRx is imperative once an app expands beyond two features. While NgRx can undoubtedly enhance an Angular application or library by simplifying debugging, translating business logic into code, and improving the architecture, it does present a steep learning curve. Despite the provocative title, there is some truth to the statement: your app or library may indeed not need NgRx. Surprisingly, I successfully developed a suite of enterprise Angular libraries over five years without involving NgRx. In that project, we decided to opt out of using a state management library like NgRx because of its steep learning curve. Developers with varying levels of Angular expertise were involved, and the goal was to simplify their experience. My bold assertion is that, with careful consideration of architectural patterns, it is entirely possible to develop a robust app or library using only Angular, without any third-party libraries. Employing select design patterns and leveraging Angular's built-in tools can yield a highly maintainable app, even without a dedicated state management library. Having shared my somewhat audacious opinion, let me now support it by outlining a few patterns that facilitate the development of a maintainable, stateful Angular application or library without NgRx. Services and the Singleton Pattern Services provided in root or a module yield a shared instance across the entire app or module, effectively rendering them singletons. This characteristic makes them ideal for managing and sharing state across components without requiring a dedicated state management tool like NgRx. Particularly, for small to medium-sized applications, a "state service" can be a straightforward and effective alternative to a comprehensive state management solution when implemented correctly. To accurately implement state in a singleton service, consider the following: - Restrict state data to private properties and expose them only through public methods or observables to prevent external mutations. Such a pattern safeguards the integrity of your state by averting unauthorized modifications. - Utilize BehaviorSubjects or signals to enable components to respond to state changes. Both BehaviorSubject and SettableSignal retain the current value and emit it to new subscribers immediately. Components can then subscribe to these to receive the current value and any subsequent updates. - Expose public methods in your service that manage state modifications to centralize the logic for updating the state and incorporate validation, logging, or other necessary side effects. - When modifying state, always return a new instance of the data rather than altering the original data. This ensures that references are broken and components that rely on change detection can accurately detect changes. Good Component Architecture Distinguish your UI components into stateful (containers) and stateless (presentational) components. Stateful components manage data and logic, while stateless components merely receive data via inputs and emit events without maintaining an internal state. Do not get dragged into the rabbit hole of anti-patterns such as input drilling or event bubbling while trying to make as many components presentational as possible. Instead, use a Data Service Layer to provide a clean abstraction over backend API calls and handle error management, data transformation, caching, and even state management where it makes sense. Although injecting a service into a component technically categorizes it as a "smart" component, segregating the data access logic into a separate service layer ultimately enhances modularity, maintainability, scalability, and testability. Immutability A best practice is to always treat your state as immutable. Instead of modifying an object or an array directly, you should create a new copy with the changes. Adhering to immutability ensures predictability and can help in tracking changes. Applying the ChangeDetectionStrategy.OnPush strategy to components whenever possible is also a good idea as it not only optimizes performance since Angular only evaluates the component for changes when its inputs change or when a bound event is triggered, but it also enforces immutability. Change detection is only activated when a different object instance is passed to the input. Leveraging Angular Router Angular's router is a powerful tool for managing application state. It enables navigation between different parts of an application, allowing parameters to be passed along, effectively using the URL as a single source of truth for your application state, which makes the application more predictable, bookmarkable, and capable of maintaining state across reloads. Moreover, components can subscribe to URL changes and react accordingly. You can also employ router resolvers to fetch data before navigating to a route, ensuring that the necessary state is loaded before the route is activated. However, think carefully about what state you store in the URL; it should ideally only contain the state essential for navigating to a specific view of your application. More ephemeral states, like UI state, should be managed in components or services. Conclusion Angular provides lots of built-in tools and features you can effectively leverage to develop robust, maintainable applications without third-party state management libraries like NgRx. While NgRx is undoubtedly a valuable tool for managing state and side effects in large, complex applications, it may not be necessary for all projects. By employing thoughtful design patterns, such as the Singleton Pattern, adhering to principles of immutability, and leveraging Angular's built-in tools like the Router and Services, you can build a highly maintainable and stateful Angular application or library....

A Guide to (Typed) Reactive Forms in Angular - Part III (Creating Custom Form Controls) cover image

A Guide to (Typed) Reactive Forms in Angular - Part III (Creating Custom Form Controls)

So far in the series, we have learned the basics of Angular Reactive forms and created some neat logic to construct and display dynamic forms. But our work is still not done yet. Whether we just want to make our controls look good and enhance them with some markup, or whether we need a more complex control than a simple textarea, input or checkbox, we'll either need to use a component library such as Angular Material Components or get familiar with the ControlValueAccessor interface. Angular Material, by the way, uses ControlValueAccessor in its components and I recommend looking into the source code if you want to learn some advanced use cases (I have borrowed a lot of their ideas in the past). In this post, however, we will build a basic custom control from scratch. A common requirement for a component that cannot be satisfied by using standard HTML markup I came across in many projects is having a searchable combobox. So let's build one. We will start by creating a new Angular component and we can do that with a handy ng cli command: ` Then we'll implement displaying data passed in the form of our FormField class we have defined earlier in a list and allowing for filtering and selecting the options: ` ` > Note: For the sake of brevity, we will not be implementing keyboard navigation and aria labels. I strongly suggest referring to W3C WAI patterns to get guidelines on the markup and behavior of an accessible combo box. While our component now looks and behaves like a combo box, it's not a form control yet and is not connected with the Angular forms API. That's where the aforementioned ControlValueAccessor comes into play along with the NG_VALUE_ACCESSOR provider. Let's import them first, update the @Component decorator to provide the value accessor, and declare that our component is going to implement the interface: ` Now, the component should complain about a few missing methods that we need to satisfy the ControlValueAccessor interface: - A writeValue method that is called whenever the form control value is updated from the forms API (e.g. with patchValue()). - A registerOnChange method, which registers a callback function for when the value is changed from the UI. - A registerOnTouched method that registers a callback function that marks the control when it's been interacted with by the user (typically called in a blur handler). - An optional setDisabledState method that is called when we change the form control disabled state- Our (pretty standard) implementation will look like the following: ` We don't have to update the template a lot, but we can add [disabled]="disabled" attribute on our button and input to disable the interactive UI elements if the provided form control was disabled. The rest of the work can be done in the component's TypeScript code. We'll call this.onTouched() in our closeListbox method, and create a value setter that updates our internal value and also notifies the model about the value change: ` You can check out the full implementation on StackBlitz. Conclusion In this series, we've explored the powerful features of Angular reactive forms, including creating and managing dynamic typed forms. We also demonstrated how to use the ControlValueAccessor interface to create custom form controls, such as a searchable combo box. This knowledge will enable you to design complex and dynamic forms in your Angular applications. While the examples provided here are basic, they serve as a solid foundation for building more advanced form controls and implementing validation, accessibility, and other features that are essential for a seamless user experience. By mastering Angular reactive forms and custom form controls, you'll be able to create versatile and maintainable forms in your web applications. If you want to further explore the topic and prefer a form of a video, you can check out an episode of JavaScript Marathon by my amazing colleague Chris. Happy coding!...

A Guide to (Typed) Reactive Forms in Angular - Part II (Building Dynamic Superforms) cover image

A Guide to (Typed) Reactive Forms in Angular - Part II (Building Dynamic Superforms)

In the first blog post of the series, we learned about Angular reactive forms and the data structures behind them. When developing real-world applications, however, you often need to leverage dynamic forms, as writing boilerplate for every form and its specific cases can be tedious and time-consuming. In certain situations, it may even be necessary to retrieve information from an API to construct the forms. In this post, we will go over a convenient abstraction we can create to build dynamic and adaptable forms without repeating boilerplate. The trick is to create a "view model" for our data and use a service to transform that data into a reactive form. I was first introduced to this approach by my friend and former teammate Thomas Duft, and I've been using it ever since. The approach outlined in the linked article worked great with untyped forms, but since now we can get our forms strictly typed, we'll want to upgrade it. And here is where it gets a bit tricky. Remember how I mentioned you shouldn't predeclare your form groups earlier? If you want to recursively create a form from a config, you just have to. And it's a dynamic form, so you cannot easily type it. To solve the issue, I devised a trick inspired by a "Super Form" suggested by Bobby Galli. Assuming we will have interfaces defined for our data, using this approach, we can create dynamic type-safe forms. First, we'll create types for our form config: ` And then we'll create some type mappings: ` And now we can use our types in a service that will take care of creating nested dynamic forms: ` And that's it. Now we can use our FormService to create forms. Let's say we have the following User model: ` We can create a form for this user from config in the following way: ` If we would check the type of userForm.value now, we would see that it's correctly inferred as: ` Outputting the Dynamic Forms To display the dynamic forms, we can write a simple component that takes the FormSection or FormField as an Input() along with our FormGroup and displays the form recursively. We can use a setter to assign either field or section property when the view model is passed into the component, so we can conveniently use them in our template. Our form component's TypeScript code will look something like this: ` And our template will reference a new form component for each section field in case we have passed in a FormSection and it will have a switch case to display the correct control in case a FormField has been passed in: ` That way, we can display the whole form just by referencing one component, such as ` Check out an example on StackBlitz. In the next (and last) post of the series, we will learn about building custom Form Controls....

A Guide to (Typed) Reactive Forms in Angular - Part I (The Basics) cover image

A Guide to (Typed) Reactive Forms in Angular - Part I (The Basics)

When building a simple form with Angular, such as a login form, you might choose a template-driven approach, which is defined through directives in the template and requires minimal boilerplate. A barebone login form using a template-driven approach could look like the following: ` ` However, when working on a user input-heavy application requiring complex validation, dynamic fields, or a variety of different forms, the template-driven approach may prove insufficient. This is where reactive forms come into play. Reactive forms employ a reactive approach, in which the form is defined using a set of form controls and form groups. Form data and validation logic are managed in the component class, which updates the view as the user interacts with the form fields. This approach requires more boilerplate but offers greater explicitness and flexibility. In this three-part blog series, we will dive into the reactive forms data structures, learn how to build dynamic super forms, and how to create custom form controls. In this first post, we will familiarize ourselves with the three data structures from the @angular/forms module: FormControl The FormControl class in Angular represents a single form control element, such as an input, select, or textarea. It is used to track the value, validation status, and user interactions of a single form control. To create an instance of a form control, the FormControl class has a constructor that takes an optional initial value, which sets the starting value of the form control. Additionally, the class has various methods and properties that allow you to interact with and control the form control, such as setting its value, disabling it, or subscribing to value changes. As of Angular version 14, the FormControl class has been updated to include support for typed reactive forms - a feature the Angular community has been wanting for a while. This means that it is now a generic class that allows you to specify the type of value that the form control will work with using the type parameter . By default, TValue is set to any, so if you don't specify a type, the form control will function as an untyped control. If you have ever updated your Angular project with ng cli to version 14 or above, you could have also seen an UntypedFormControl class. The reason for having a UntypedFormControl class is to support incremental migration to typed forms. It also allows you to enable types for your form controls after automatic migration. Here is an example of how you may initialize a FormControl in your component. ` Our form control, in this case, will work with string values and have a default value of "John Doe". If you want to see the full implementation of the FormControl class, you can check out the Angular docs! FormGroup A FormGroup is a class used to group several FormControl instances together. It allows you to create complex forms by organizing multiple form controls into a single object. The FormGroup class also provides a way to track the overall validation status of the group of form controls, as well as the value of the group as a whole. A FormGroup instance can be created by passing in a collection of FormControl instances as the group's controls. The group's controls can be accessed by their names, just like the controls in the group. As an example, we can rewrite the login form presented earlier to use reactive forms and group our two form controls together in a FormGroup instance: ` ` Notice we have to specify a formGroup and a formControlName to map the markup to our reactive form. You could also use a formControl directive instead of formControlName, and provide the FormControl instance directly. FormArray As the name suggests, similar to FormGroup, a FormArray is a class used to group form controls, but is used to group them in a collection rather than a group. In most cases, you will default to using a FormGroup but a FormArray may come in handy when you find yourself in a highly dynamic situation where you don't know the number of form controls and their names up front. One use case where it makes sense to resort to using FormArray is when you allow users to add to a list and define some values inside of that list. Let's take a TODO app as an example: ` In both of the examples provided, we instantiate FormGroup directly. However, some developers prefer to pre-declare the form group and assign it within the ngOnInit method. This is usually done as follows: ` If you try the above example in your IDE, you'll notice that the type of this.form.value is no longer inferred, and you won't get autocompletion for methods such as patchValue. This is because the FormGroup type defaults to FormGroup. To get the right types, you can either assign the form group directly or explicitly declare the generics like so: ` However, explicitly typing all your forms like this can be inconvenient and I would advise you to avoid pre-declaring your FormGroups if you can help it. In the next blog post, we will learn a way to construct dynamic super forms with minimal boilerplate....

A Guide to Custom Angular Attribute Directives cover image

A Guide to Custom Angular Attribute Directives

Discover the power of Angular attribute directives and learn how to create your own custom directives with this interactive guide from This Dot Labs...

How to Create Standalone Components in Angular cover image

How to Create Standalone Components in Angular

Delve into the power of Angular's component-based architecture, focusing on standalone components that facilitate modularity and reusability....

Introduction to Directives Composition API in Angular cover image

Introduction to Directives Composition API in Angular

In version 15, Angular introduced a new directives composition API that allows developers to compose existing directives into new more complex directives or components. This allows us to encapsulate behaviors into smaller directives and reuse them across the application more easily. In this article, we will explore the new API, and see how we can use it in our own components. All the examples from this article (and more) can be found on Stackblitz here. Starting point In the article, I will use two simple directives as an example * HighlightDirective - a directive borrowed from Angular's Getting started guide. This directive will change an element's background color whenever the element hovers. ` Fig. 1 * BorderDirective - a similar directive that will apply a border of a specified color to the element whenever it hovers ` Fig. 2 We can now easily apply our directives to any element we want ie. a paragraph: ` Fig. 3 However, if we wanted to apply both highlighting and border on hover we would need to add both directives explicitly: ` Fig. 4 With the new directives composition API, we can easily create another directive that composes behaviors of our 2 directives. Host Directives Angular 15 added a new property to the @Directive and @Component decorators. In this property, we can specify an array of different directives that we want our new component or directive to apply on a host element. We can do it as follows: ` As you can see in the above example, by just defining the hostDirectives property containing our highlight and border directives, we created a new directive that composes both behaviors into one directive. We can now achieve the same result as in Fig. 4 by using just a single directive: ` Fig. 5 Passing inputs and outputs Our newly composed directive works nicely already, but there is a problem. How do we pass properties to the directives that are defined in the hostDirectives array? They are not passed by default, but we can configure them to do so pretty easily by using an extended syntax: ` Fig. 6 This syntax takes exposes the "inner" directives\ color input from the HighlightAndBorderDirective`, and passes them down to both highlight and border directives. ` Fig. 7 This works, but we ended up with a border and highlight color both being blue. Luckily Angular's API allows us to easily redefine the properties' names using the : syntax. So let's remap out properties to highlightColor and borderColor so that the names don't collide with each other: ` Fig. 8 Now we can control both colors individually: ` Fig. 9 We could apply the same approach to mapping directive's outputs eg. ` Fig. 10 or ` Fig. 11 Adding host directives to a component Similarly to composing a directive out of other directives, we can apply the same approach to adding behavior to components using hostDirectives API. This way, we could, for example, create a more specialized component or just apply the behavior of the directive to a whole host element: ` Fig. 12 This component will render the paragraph, and apply both directives' behavior to the host element: ` Fig. 13 Just like we did for the directive, we can also expose and remap the directives inputs using the extended syntax. But if we would like to access and modify the directives inputs from within our component, we can also do that. This is where Angular's dependency injection comes in handy. We can inject the host directives via a constructor just like we would do for a service. After we have the directives instances available, we can modify them ie. in the ngOnInit lifecycle hook: ` Fig. 14 With this change, the code from Fig. 13 will use lightcoral as a background color and red as a border color. Performance Note While this API gives us a powerful tool-set for reusing behaviors across different components, it can impact the performance of our application if used excessively. For each instance of a given composed component Angular will create objects of the component class itself as well as an instance of each directive that it is composed of. If the component appears only a couple of times in the application. then it won't make a significant difference. However, if we create, for example, a composed checkbox component that appears hundreds of times in the app, this may have a noticeable performance impact. Please make sure you use this pattern with caution, and profile your application in order to find the right composition pattern for your application. Summary As I have shown in the above examples, the directives composition API can be a quite useful but easy-to-use tool for extracting behaviors into smaller directives and combining them into more complex behaviors. In case you have any questions, you can always tweet or DM me at @ktrz. I'm always happy to help!...

Using a Starter Kit on to Kickstart Your React and Angular Projects cover image

Using a Starter Kit on to Kickstart Your React and Angular Projects

As a developer, deciding on the stack to use and the dependencies can be a difficult choice. Once you start, configuration, installation, and making sure you have the correct versions are also chores. Starting with a prebuilt framework for some of these tasks can boost a developer's productivity and save time by eliminating repetitions. In this article, I will walk you through how you can cut 40 hours of setup and boost productivity by focusing on what really matters. We will learn a simple solution for implementing project functionality without worrying about setting up different versions of dependencies and configurations, or repeating the same process for different projects. Introducing Starter Kits has a curated list of starter kits for various frameworks and libraries to help boost developer productivity. Why starter kits? Our architects discovered they were repeatedly going through the same process every time they needed to start a new project. This process included initializing, installing dependencies, setting up tests, and everything else that comes with bootstrapping a new project. How to use a Starter Kit Let’s walk through starting apps with Angular, and React with starter kits using the Starter Kits Showcases. Every starter kit is structured with the basic requirements to initialize the projects with key features: - Routing - Forms - State Management - API interactions - REST & GraphQL - Authentication - Test Runners These features help developers understand how to architect and build large scale applications. The default project directories are scaffolded with added directories, configurations, and a demo app to help quickly build or modify the components. Using the Angular Apollo TailwindCSS Starter Kit Every starter kit is easy to initialize so you can start building features for your project. The Angular starter kit sets you up with these tools to quickly get you started: - Angular 13: for build frontend components - Apollo Client: for managing both local and remote data with GraphQL - TailwindCSS: for styling components and elements - Storybook: Component library - Jest: Test runner Initializing the project: To initialize a project with the starter kits, run the following: 1. Run npx @this-dot/create-starter to run the scaffolding tool 2. Select Angular, Apollo, and TailwindCSS kits from the CLI library options 3. Name your project 4. cd into your project directory, install dependencies using the tool of your choice, and voila! You can start building features in your new project immediately. Configurations The project pre-configuration is the recommended Angular GraphQL configuration. The ApolloModule and ApolloClientOptions are imported for the App NgModule. ` ` Project Implementation. Starter kits aim to make your project set up fast, easy and reusable. The initial demo project is a counter example connected to a GraphQL module with a fetch example and routes implemented - all following industry’s best practices. Go ahead to add more components or modify existing components without the need to make any changes to the configuration. Extras The preconfigured Storybook for component management gives developers the opportunity to create organized UI systems making both the building process and documentation more efficient and easier to use. React RxJs and Styled Components The React starter kit tools scaffold to quickly get you started with a React project, and include: - React 17: for building frontend components - RxJs: for managing data - Styled Components: for styling components and elements - React Router: For matching project routes - Storybook: Component library - Jest: Test runner Initializing the project: To initialize a project with the starter kits, run the following: - Run npx @this-dot/create-starter to run the scaffolding tool - Select Create React App, RxJS and Styled Components kits from the CLI library options - Name your project - cd into your project directory, install dependencies using the tool of your choice, and voila! You can start building features in your new project immediately. Configurations The project configuration is the same with an initial create-react-app config boilerplate, but with pre-configurations for Storybook and style component. Conclusion In this article, you were able to boost productivity by cutting the time of setting up and moving straight to implementing components, allowing you to focus on working on what matters. I hope this article was helpful, let us know what other starter kits you will love to see on

Angular vs React Comparing Route Prefetching cover image

Angular vs React Comparing Route Prefetching

Angular vs React Comparing Route Prefetching At times during the development lifecycle, we need to perform specific strategies that accomplish a desired UX. A user may need to perform a task in the near future where the data is needed so we establish a browser cache for the necessary resources. Whether at the beginning of the app's initialization or during navigation, we can provide everything upfront or lazily as needed. A number of frameworks and libraries allow for pre-fetching strategies as part of its packaging and they perform those tasks fluidly or require a few handshakes to make it possible. We'll compare how this can be done in Angular and React as well as some others like Remix and Vue. Before we go too much further, let's understand the advantages of pre-fetching and when we should perform them. What is Prefetching? As mentioned briefly, data prefetching accomplishes two objectives: * data storage by caching in the browser * seemingly on-time retrieval by the user When a user performs navigation whether initializing a web app or navigating within the app, the app either loads all or a portion of data. However, this isn't without cost or impact. This data still requires network bandwidth for the transfer, processing time, and the cache. If a user decides to not use a portion of a web app but this data has been made available to them, performance is diminished. When is Prefetching Useful? While we know about the browser's limited caching resources and overall performance considerations for prefetching, the decision still needs to be made about whether or not to perform pre-fetching. A question to keep in mind is: "does enabling prefetch increase or decrease the utility of the app?" Determining the strategy to use will make an app sink under load times or swim with the right balance of load time and page rendering. Angular Prefetch with Route Resolver Angular has a relatively simple setup with its loading strategy. The resolver pattern binds data loading to a route where any navigation to the guarded route or its branches first loads the resolver and uses a snapshot of the route's data for use during the lifecycle of any components within that route. However this has limitations because the data isn't globally available to other routes. Take, for instance, a User service and route that had a resolve guard. In order to access a user's data, some pre-requisites need to be available first. First a basic example resolver takes this shape. *user-resolver.service.ts* ` And it called on the route guarded route *user-route.ts* ` Accessing the data in user now looks like this in the component where we look at the currently activated route for a hydrated data['user'] property: ` This approach to prefetching doesn't take into account Server-Side Rendering and is purely client side. Projects like Angular Universal make it possible to perform some controlled data flows and setup than the client-side fetching would allow. Considerations for Global State We can overcome ths issue of limited app-wide or global state with a couple techniques: 1. A data service layer that utilizes caching 2. Lifting state to parent routes within the route tree 3. Use state management libraries While each of these strategies has advantages over the other, most developers tend to consider libraries like NgRx and Akita for Angular before thinking about the performance and overall impact these mechanisms add to the application. Prefetching in React If you did a Google search for prefetching in React, you'd probably get a number of developer posts on either React's experimental feature or you'll find resources on running flavoured extensions of React like NextJS and Remix. This is to say, due to React's "choose your adventure" ecosystem, it doesn't have its own implementation readily available thus needing to create your own. As mentioned, you could choose a flavour that best suites your needs. We'll briefly discuss the similarities and differences between Angular and NextJS versions of prefetching in making an architectural decision. Client-side Prefetching A combination of react-router and useContext is a way to emulate what Angular does with its protected routes. An example comparable to the earlier resolver pattern looks like setting up a protected route: *protected-route.tsx* ` Applying the new "route guard": *app.tsx* ` Add CurrentUserContext to act as the activated route's data handler: *app.tsx* ` Then use the context in a child component: *login-button.tsx* ` When the user performs a click to "Log in", they be set with a user that's provided throughout the app. In this instance, we set it up globally because it recommended to have all providers as high up the component tree as possible. Considering Server-side Rendering (SSR) SSR is a great way to increase the performance of applications by making sure dynamic data is always up-to-date and hydrated. It makes the page interactive by rendering content, and with a bit a JavaScript, to make it dynamic. This impact is two-fold as it improves SEO and performance in page load times. Regardless of the applications, utilizing this mechanism, we know the outcome is the same. SSR is a great way to extend the capabilities of the client-fetch strategies addressed earlier as they leverage the same content, but just make rendering requests at a controlled server level. Ideally, one could load authorization, headers, localizations for languages, perform caching, and load data from a CMS. Angular Universal Angular Universal is an extension to a base Angular project by including an extra level of control. This involves adding the ability to perform server rendering capabilities to an Angular app via an express server extension. It shares similar context to NextJS's implementation of SSR although the pattern is a bit more verbose in a server.ts file generated with the following commands: ` ` Routes specified within the server.ts file can perform a number of server-related tasks standard to writing an express app. This also includes limitations like using browser API because server code doesn't run in the browser context. Therefore, window, document, navigator, and location is not available *directly*. Angular provides these as injectable. Lastly, Angular Universal can handle Statically-Generated routes as well. However, an argument can be made for the effectiveness of this in Angular given how heavy a framework it is compared to other, smaller applications that handle this more efficiently. NextJS NextJS is a great tool compared to bare React when determining the cost-effectiveness of setup and ease. While one could use this framework to justify a client-side fetching strategy, it's unnecessary if all the performant features aren't used like its version "prefetching". Before embarking on prefetching, consider that NextJS provides pre-rendering in two contexts: Static Generation and Server-side Rendering similarly to Angular Universal. While each one has a specific use, the most effective one for this concept is Server-side Rendering because most scalable, enterprise applications rely on fresh, dynamic data.... A Community-Led Open Source Project Welcoming to All Developers cover image A Community-Led Open Source Project Welcoming to All Developers is way more than just another resource list because it is a place to learn, grow, connect and be inspired....