<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Brian Love</title>
    <description>The latest articles on Forem by Brian Love (@blove).</description>
    <link>https://forem.com/blove</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F290639%2F48678cb0-bea9-4983-aab7-c93500e78b45.jpg</url>
      <title>Forem: Brian Love</title>
      <link>https://forem.com/blove</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/blove"/>
    <language>en</language>
    <item>
      <title>Agentic Memory and What It Means for Web Apps</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 18 Mar 2026 23:02:39 +0000</pubDate>
      <link>https://forem.com/blove/agentic-memory-and-what-it-means-for-web-apps-2hea</link>
      <guid>https://forem.com/blove/agentic-memory-and-what-it-means-for-web-apps-2hea</guid>
      <description>&lt;p&gt;Memory is becoming one of the most important design surfaces in agentic software.&lt;/p&gt;

&lt;p&gt;Not because models suddenly became databases.&lt;br&gt;
And not because storing more transcripts is the same thing as making a system smarter.&lt;/p&gt;

&lt;p&gt;It matters because memory changes what kind of software we are building.&lt;/p&gt;

&lt;p&gt;A stateless LLM can answer.&lt;br&gt;
A system with agentic memory can improve.&lt;/p&gt;

&lt;p&gt;That is a different class of product.&lt;/p&gt;

&lt;p&gt;For me, the core idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory turns LLMs from stateless responders into stateful systems&lt;/li&gt;
&lt;li&gt;memory is a form of non-parametric learning&lt;/li&gt;
&lt;li&gt;the hard problem is no longer storing more context&lt;/li&gt;
&lt;li&gt;the hard problem is deciding what should be remembered, when, and in what form&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building fullstack agentic web applications, this is the shift to pay attention to.&lt;/p&gt;

&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agentic memory is not just retrieval. It is an agent capability to store, retrieve, update, summarize, and delete knowledge over time.&lt;/li&gt;
&lt;li&gt;This gives us a practical form of continual learning without fine-tuning model weights.&lt;/li&gt;
&lt;li&gt;For web apps, memory changes architecture, not just prompting. It affects UX, data modeling, evaluation, governance, and trust.&lt;/li&gt;
&lt;li&gt;The right production model is usually typed memory, selective retrieval, background consolidation, and immutable raw history behind derived memory.&lt;/li&gt;
&lt;li&gt;Memory is becoming an agent policy surface. That means memory quality will matter as much as model quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Angular Agent Framework
&lt;/h2&gt;

&lt;p&gt;Check out CachePlane's &lt;a href="https://cacheplane.ai" rel="noopener noreferrer"&gt;Angular Agent Framework&lt;/a&gt; at cacheplane.ai for building agentic fullstack Angular applications using LangChain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why memory changes everything
&lt;/h2&gt;

&lt;p&gt;Most LLM applications started as stateless request-response systems.&lt;/p&gt;

&lt;p&gt;That was fine for summarization, classification, and one-shot chat.&lt;br&gt;
It is not enough for software that is supposed to improve over time.&lt;/p&gt;

&lt;p&gt;As soon as you want an agent to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remember user preferences&lt;/li&gt;
&lt;li&gt;reuse successful workflows&lt;/li&gt;
&lt;li&gt;avoid repeated failures&lt;/li&gt;
&lt;li&gt;carry state across sessions&lt;/li&gt;
&lt;li&gt;personalize behavior without retraining&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;you need memory.&lt;/p&gt;

&lt;p&gt;And not memory in the casual sense of "we saved the conversation somewhere."&lt;/p&gt;

&lt;p&gt;You need a system where the agent can actively manage what it knows.&lt;/p&gt;

&lt;p&gt;That is what makes the memory &lt;em&gt;agentic&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What agentic memory actually is
&lt;/h2&gt;

&lt;p&gt;Agentic memory is a system where an agent can decide to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;store something&lt;/li&gt;
&lt;li&gt;retrieve something&lt;/li&gt;
&lt;li&gt;update something&lt;/li&gt;
&lt;li&gt;summarize something&lt;/li&gt;
&lt;li&gt;delete something&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point matters.&lt;br&gt;
If the system can only append, it does not really have memory discipline.&lt;br&gt;
It has a log.&lt;/p&gt;

&lt;p&gt;This is why I think the right framing is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory is not storage. It is a control surface for reasoning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the real shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic memory is like cramming for a test
&lt;/h2&gt;

&lt;p&gt;One of the more important ideas here is that memory gives us a form of continual learning without changing model weights.&lt;/p&gt;

&lt;p&gt;The model does not need to be fine-tuned every time it learns something useful.&lt;br&gt;
It can improve by pulling the right memories into context at inference time.&lt;/p&gt;

&lt;p&gt;That is why I think of memory as test-time learning.&lt;/p&gt;

&lt;p&gt;Different systems approach it differently, but the common idea is the same:&lt;br&gt;
the agent gets better because it can reuse abstractions learned from prior experience.&lt;/p&gt;

&lt;p&gt;That is a much more practical path for product teams than constant retraining.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three memory types that matter
&lt;/h2&gt;

&lt;p&gt;I think it is useful to separate memory into three buckets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Semantic memory&lt;/li&gt;
&lt;li&gt;Episodic memory&lt;/li&gt;
&lt;li&gt;Procedural memory&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Semantic memory
&lt;/h3&gt;

&lt;p&gt;Semantic memory is facts, preferences, constraints, and stable knowledge.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preferred output format&lt;/li&gt;
&lt;li&gt;user role&lt;/li&gt;
&lt;li&gt;account rules&lt;/li&gt;
&lt;li&gt;domain terminology&lt;/li&gt;
&lt;li&gt;known business constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the memory type that drives correctness and personalization.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Episodic memory
&lt;/h3&gt;

&lt;p&gt;Episodic memory is past experiences.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a successful prior resolution for a similar support issue&lt;/li&gt;
&lt;li&gt;a failed workflow and the correction that fixed it&lt;/li&gt;
&lt;li&gt;a previous user interaction pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the memory type that helps reasoning by analogy.&lt;br&gt;
It is how agents get a practical form of "I have seen something like this before."&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Procedural memory
&lt;/h3&gt;

&lt;p&gt;Procedural memory is behavior.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preferred prompts&lt;/li&gt;
&lt;li&gt;tool-use patterns&lt;/li&gt;
&lt;li&gt;routing rules&lt;/li&gt;
&lt;li&gt;safety policies&lt;/li&gt;
&lt;li&gt;execution instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the memory type that improves consistency.&lt;/p&gt;

&lt;p&gt;I think this separation matters because different memory types want different storage, retrieval, and evaluation strategies.&lt;/p&gt;

&lt;p&gt;If you flatten them all into one vector store, you are usually making retrieval worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for web apps
&lt;/h2&gt;

&lt;p&gt;This is the part I care about most.&lt;/p&gt;

&lt;p&gt;Agentic memory is not just an infra feature for backend agents.&lt;br&gt;
It changes how web apps should be designed.&lt;/p&gt;

&lt;p&gt;A web app with agentic memory is not just rendering model output.&lt;br&gt;
It is participating in a learning loop.&lt;/p&gt;

&lt;p&gt;The frontend becomes the place where memory is created, corrected, and validated.&lt;/p&gt;

&lt;p&gt;That has a few practical implications.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Web apps become memory surfaces
&lt;/h3&gt;

&lt;p&gt;The frontend sees things the model and backend often do not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what the user accepted&lt;/li&gt;
&lt;li&gt;what they edited&lt;/li&gt;
&lt;li&gt;what they rejected&lt;/li&gt;
&lt;li&gt;how long they hesitated&lt;/li&gt;
&lt;li&gt;where they retried&lt;/li&gt;
&lt;li&gt;when they abandoned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are memory candidates.&lt;/p&gt;

&lt;p&gt;Not all of them should be stored.&lt;br&gt;
But the web app is where those signals become visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Personalization becomes a first-class product system
&lt;/h3&gt;

&lt;p&gt;Personalization used to mean feature flags, settings, and saved preferences.&lt;/p&gt;

&lt;p&gt;Now it also means memory.&lt;/p&gt;

&lt;p&gt;The agent should be able to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how a person likes information presented&lt;/li&gt;
&lt;li&gt;what defaults they repeatedly choose&lt;/li&gt;
&lt;li&gt;what kinds of actions they permit or avoid&lt;/li&gt;
&lt;li&gt;what vocabulary is normal in their context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a better product experience.&lt;br&gt;
It is also a new governance problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Multi-session coherence becomes a UX expectation
&lt;/h3&gt;

&lt;p&gt;Once users see an agent remember important context, they start expecting continuity.&lt;/p&gt;

&lt;p&gt;That means the web app needs to help answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what should persist across sessions?&lt;/li&gt;
&lt;li&gt;what should expire?&lt;/li&gt;
&lt;li&gt;what should be editable by the user?&lt;/li&gt;
&lt;li&gt;what should be visible as remembered state?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why memory is also a UX problem, not just a systems problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Context engineering becomes product infrastructure
&lt;/h3&gt;

&lt;p&gt;I think "context engineering" is one of the most useful phrases in this space.&lt;/p&gt;

&lt;p&gt;The problem is no longer just fitting more tokens into a prompt.&lt;br&gt;
The problem is selecting the right abstractions.&lt;/p&gt;

&lt;p&gt;Bad memory systems create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;context poisoning&lt;/li&gt;
&lt;li&gt;distraction&lt;/li&gt;
&lt;li&gt;token waste&lt;/li&gt;
&lt;li&gt;conflicting guidance&lt;/li&gt;
&lt;li&gt;brittle personalization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good memory systems do the opposite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;selective retrieval&lt;/li&gt;
&lt;li&gt;summarization&lt;/li&gt;
&lt;li&gt;distillation&lt;/li&gt;
&lt;li&gt;isolation by scope&lt;/li&gt;
&lt;li&gt;time-aware filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why I would argue:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem is no longer remembering more. It is remembering the right abstractions.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflection is where memory becomes learning
&lt;/h2&gt;

&lt;p&gt;One of the most important loops in agent systems is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;act&lt;/li&gt;
&lt;li&gt;observe&lt;/li&gt;
&lt;li&gt;critique&lt;/li&gt;
&lt;li&gt;store&lt;/li&gt;
&lt;li&gt;reuse&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is reflection.&lt;/p&gt;

&lt;p&gt;In practice, a lot of the gains in agent quality come from this loop.&lt;br&gt;
The agent does something, observes success or failure, stores the useful lesson, and applies it later.&lt;/p&gt;

&lt;p&gt;This is why grounded reflection matters so much.&lt;br&gt;
If the critique comes from real environment feedback, user behavior, or verifiable outcomes, the memory is much more useful than a purely self-generated summary.&lt;/p&gt;

&lt;p&gt;This is also why the web app matters so much.&lt;br&gt;
It is often the best place to observe the real outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture I would actually ship
&lt;/h2&gt;

&lt;p&gt;If I were building agentic memory into a web app today, I would not start with one giant memory store.&lt;/p&gt;

&lt;p&gt;I would use a layered design:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Short-term thread memory&lt;/li&gt;
&lt;li&gt;Long-term typed memory&lt;/li&gt;
&lt;li&gt;Immutable raw history&lt;/li&gt;
&lt;li&gt;Background consolidation&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Short-term thread memory
&lt;/h3&gt;

&lt;p&gt;This is the active working set for the current task or session.&lt;/p&gt;

&lt;p&gt;Use it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recent messages&lt;/li&gt;
&lt;li&gt;in-progress tool state&lt;/li&gt;
&lt;li&gt;temporary planning context&lt;/li&gt;
&lt;li&gt;current UI state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is hot memory.&lt;br&gt;
Fast in, fast out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long-term typed memory
&lt;/h3&gt;

&lt;p&gt;This is where semantic, episodic, and procedural memories live separately.&lt;/p&gt;

&lt;p&gt;Use it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user preferences&lt;/li&gt;
&lt;li&gt;reusable examples&lt;/li&gt;
&lt;li&gt;learned task heuristics&lt;/li&gt;
&lt;li&gt;stable operating policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where I want stronger structure and stronger retrieval rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutable raw history
&lt;/h3&gt;

&lt;p&gt;Never trust repeated summarization as your only source of truth.&lt;/p&gt;

&lt;p&gt;Summaries drift.&lt;br&gt;
Compression loses nuance.&lt;br&gt;
Derived memory can get subtly wrong over time.&lt;/p&gt;

&lt;p&gt;So I want a raw, immutable log behind the optimized memory layer.&lt;/p&gt;

&lt;p&gt;That gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auditability&lt;/li&gt;
&lt;li&gt;rollback&lt;/li&gt;
&lt;li&gt;better debugging&lt;/li&gt;
&lt;li&gt;safer reprocessing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Background consolidation
&lt;/h3&gt;

&lt;p&gt;Not every memory write should happen synchronously in the request path.&lt;/p&gt;

&lt;p&gt;Some should.&lt;br&gt;
Others should be consolidated later.&lt;/p&gt;

&lt;p&gt;That is the hot + cold model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;synchronous writes for critical immediate context&lt;/li&gt;
&lt;li&gt;asynchronous consolidation for summarization, distillation, and indexing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is usually the right tradeoff between latency and memory quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patterns I like
&lt;/h2&gt;

&lt;p&gt;There are a few patterns here that I think are especially practical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hot + cold memory
&lt;/h3&gt;

&lt;p&gt;Write immediately when the task needs it.&lt;br&gt;
Consolidate later when quality matters more than latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distilled memory
&lt;/h3&gt;

&lt;p&gt;Do not store raw transcripts as the primary memory object if what you really need is a reusable abstraction.&lt;/p&gt;

&lt;p&gt;Store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the lesson&lt;/li&gt;
&lt;li&gt;the source&lt;/li&gt;
&lt;li&gt;the timestamp&lt;/li&gt;
&lt;li&gt;the confidence&lt;/li&gt;
&lt;li&gt;the scope&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is much more useful than dumping an entire conversation into retrieval.&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutable + derived memory
&lt;/h3&gt;

&lt;p&gt;I trust systems more when they keep both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;immutable raw events&lt;/li&gt;
&lt;li&gt;derived summaries and optimized memories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is how you keep memory systems from becoming opaque.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory graphs
&lt;/h3&gt;

&lt;p&gt;Similarity search is useful, but it is not enough.&lt;/p&gt;

&lt;p&gt;Some memories are connected by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;causality&lt;/li&gt;
&lt;li&gt;sequence&lt;/li&gt;
&lt;li&gt;dependency&lt;/li&gt;
&lt;li&gt;contradiction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Graph-shaped memory is much better at expressing that than naive top-k vector retrieval.&lt;/p&gt;

&lt;p&gt;I expect more systems to move in this direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The production risks are real
&lt;/h2&gt;

&lt;p&gt;Memory makes systems better.&lt;br&gt;
It also makes them more dangerous.&lt;/p&gt;

&lt;p&gt;At least four risks matter immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Retrieval quality
&lt;/h3&gt;

&lt;p&gt;Just because something is semantically similar does not mean it is operationally relevant.&lt;/p&gt;

&lt;p&gt;Memory retrieval often misses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;causal relevance&lt;/li&gt;
&lt;li&gt;implicit constraints&lt;/li&gt;
&lt;li&gt;temporal change&lt;/li&gt;
&lt;li&gt;contradictory updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why memory quality is usually more important than memory volume.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Memory drift
&lt;/h3&gt;

&lt;p&gt;If you repeatedly summarize summaries, you eventually distort the original meaning.&lt;/p&gt;

&lt;p&gt;That is why derived memory needs provenance and raw backing data.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Security
&lt;/h3&gt;

&lt;p&gt;Memory injection is a real design concern.&lt;/p&gt;

&lt;p&gt;If an attacker can poison memory, they can shape future agent behavior.&lt;/p&gt;

&lt;p&gt;This means memory systems need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;trust boundaries&lt;/li&gt;
&lt;li&gt;scoped access&lt;/li&gt;
&lt;li&gt;deletion paths&lt;/li&gt;
&lt;li&gt;source attribution&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Evaluation
&lt;/h3&gt;

&lt;p&gt;A memory system can look impressive in a demo and still fail long-horizon tasks in production.&lt;/p&gt;

&lt;p&gt;We still need better evaluation for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-session behavior&lt;/li&gt;
&lt;li&gt;long-horizon execution&lt;/li&gt;
&lt;li&gt;memory usefulness over time&lt;/li&gt;
&lt;li&gt;robustness to stale or conflicting memories&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Memory governance is now part of application architecture
&lt;/h2&gt;

&lt;p&gt;This is the part I think teams will underestimate.&lt;/p&gt;

&lt;p&gt;As soon as memory affects behavior, governance matters.&lt;/p&gt;

&lt;p&gt;You need clear rules for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what gets stored&lt;/li&gt;
&lt;li&gt;who can access it&lt;/li&gt;
&lt;li&gt;how it decays&lt;/li&gt;
&lt;li&gt;how it is corrected&lt;/li&gt;
&lt;li&gt;how it is deleted&lt;/li&gt;
&lt;li&gt;how it is explained to the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is true for enterprise software.&lt;br&gt;
It is even more true for consumer software.&lt;/p&gt;

&lt;p&gt;The best systems will not just remember well.&lt;br&gt;
They will remember responsibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  My practical recommendations
&lt;/h2&gt;

&lt;p&gt;If you are building agentic memory into a web app now, this is the sequence I would use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Separate semantic, episodic, and procedural memory.&lt;/li&gt;
&lt;li&gt;Keep immutable raw history behind derived memory.&lt;/li&gt;
&lt;li&gt;Prefer distilled memory objects over raw transcript retrieval.&lt;/li&gt;
&lt;li&gt;Add time, source, scope, and version to every stored memory.&lt;/li&gt;
&lt;li&gt;Use synchronous writes sparingly and background consolidation aggressively.&lt;/li&gt;
&lt;li&gt;Tune retrieval strategy by memory type instead of using one global approach.&lt;/li&gt;
&lt;li&gt;Evaluate on multi-session and long-horizon tasks, not only single-turn quality.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the difference between "we added memory" and "we built a memory system."&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Agentic memory changes the role of the model.&lt;br&gt;
It also changes the role of the web app.&lt;/p&gt;

&lt;p&gt;The web app is no longer just a place where model output gets rendered.&lt;br&gt;
It is where memory is shaped, corrected, surfaced, and governed.&lt;/p&gt;

&lt;p&gt;That is why I think memory is going to become foundational to intelligent software.&lt;/p&gt;

&lt;p&gt;Not because remembering more is inherently better.&lt;br&gt;
But because the right memory architecture lets software learn without pretending every improvement requires retraining.&lt;/p&gt;

&lt;p&gt;Memory is becoming policy.&lt;br&gt;
And policy is becoming product behavior.&lt;/p&gt;

&lt;p&gt;That is what makes this interesting.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Google A2UI: Fixed Schemas, Dynamic Schemas, and a Safe Fallback Strategy</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 18 Mar 2026 23:02:39 +0000</pubDate>
      <link>https://forem.com/blove/google-a2ui-fixed-schemas-dynamic-schemas-and-a-safe-fallback-strategy-284f</link>
      <guid>https://forem.com/blove/google-a2ui-fixed-schemas-dynamic-schemas-and-a-safe-fallback-strategy-284f</guid>
      <description>&lt;p&gt;Google's A2UI is one of the more interesting protocol ideas in agentic UI right now.&lt;/p&gt;

&lt;p&gt;Not because it lets a model generate arbitrary frontend code.&lt;br&gt;
That would be a fast way to move chaos across a trust boundary.&lt;/p&gt;

&lt;p&gt;It is interesting because it does the opposite.&lt;/p&gt;

&lt;p&gt;A2UI gives the agent a declarative way to describe UI while keeping rendering inside a trusted client-owned component system.&lt;/p&gt;

&lt;p&gt;That is the part that matters.&lt;br&gt;
Especially if you are building a real product instead of a demo.&lt;/p&gt;

&lt;p&gt;The practical question is not whether A2UI is good.&lt;br&gt;
The practical question is how to use it without turning your UI contract into a probabilistic event.&lt;/p&gt;

&lt;p&gt;For me, that comes down to one decision:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fixed schema&lt;/li&gt;
&lt;li&gt;Dynamic schema&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My recommendation is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use fixed schemas by default.&lt;/li&gt;
&lt;li&gt;Use dynamic schema overlays for the long tail.&lt;/li&gt;
&lt;li&gt;Validate everything.&lt;/li&gt;
&lt;li&gt;Retry a few times.&lt;/li&gt;
&lt;li&gt;Fall back deterministically.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A2UI is a protocol for agent-driven interfaces where the client owns rendering and the model emits declarative UI messages.&lt;/li&gt;
&lt;li&gt;A2UI v0.9 is more model-friendly than earlier versions because it is flatter, more prompt-friendly, and easier to validate.&lt;/li&gt;
&lt;li&gt;Most teams should start with fixed schemas because they are easier to test, easier to operate, and easier to trust.&lt;/li&gt;
&lt;li&gt;Dynamic schemas are useful, but only when they stay grounded in renderer-approved catalogs.&lt;/li&gt;
&lt;li&gt;The right production posture is fixed by default, dynamic overlays for the long tail, and deterministic fallback when validation fails.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Angular Fullstack Agentic Frameowork
&lt;/h2&gt;

&lt;p&gt;Be sure to check out the &lt;a href="https://cacheplane.ai" rel="noopener noreferrer"&gt;Angular Agent Framework&lt;/a&gt; for building fullstack agentic applications using Angular. A2UI support will be landing soon!&lt;/p&gt;
&lt;h2&gt;
  
  
  Fullstack architecture sketch
&lt;/h2&gt;

&lt;p&gt;Before getting into fixed versus dynamic schema, this is the deployment shape I would use:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5jas8zz4jxxpxmjwkwk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5jas8zz4jxxpxmjwkwk.png" alt="Animated fullstack architecture diagram for A2UI showing the trusted web client, the API and agent backend, and the observability layer." width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web Client
  - trusted A2UI renderer
  - advertises supported catalogs
  - may advertise inline catalogs
  - sends actions and data model updates

API / Agent Backend
  - receives client capabilities
  - selects fixed vs dynamic policy
  - builds prompt from selected catalog
  - runs schema repair loop when dynamic
  - validates final payloads
  - streams safe A2UI messages to client

Observability
  - schema validation failures
  - payload validation failures
  - fallback frequency
  - per-intent success rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why A2UI matters
&lt;/h2&gt;

&lt;p&gt;A2UI matters because it gives us a cleaner split between model behavior and frontend control.&lt;/p&gt;

&lt;p&gt;The model decides what interface should appear.&lt;br&gt;
The client decides how that interface is rendered.&lt;/p&gt;

&lt;p&gt;That is a much better contract than shipping arbitrary frontend code across the wire and pretending the security story will work itself out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgwmd80wdja8ncvuhjfw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgwmd80wdja8ncvuhjfw2.png" alt="Animated diagram of A2UI where the agent emits declarative messages, the backend validates the contract, and the trusted client renderer owns rendering." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why v0.9 is the interesting version
&lt;/h2&gt;

&lt;p&gt;One caveat up front:&lt;br&gt;
A2UI v0.9 is still a draft, while v0.8 is the stable production release.&lt;/p&gt;

&lt;p&gt;So this is not a "ship and forget" protocol.&lt;br&gt;
You should pin versions and test contracts carefully.&lt;/p&gt;

&lt;p&gt;But v0.9 is still the version I would watch, because it is the version that feels much more usable for agent systems.&lt;/p&gt;

&lt;p&gt;The notable shifts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a prompt-first design&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createSurface&lt;/code&gt; replacing &lt;code&gt;beginRendering&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a flat component model with a &lt;code&gt;component&lt;/code&gt; discriminator&lt;/li&gt;
&lt;li&gt;more standardized &lt;code&gt;updateDataModel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;swappable catalogs instead of a single hardwired shape&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes the protocol easier to prompt, easier to generate, and easier to validate.&lt;br&gt;
That is the real win.&lt;/p&gt;
&lt;h2&gt;
  
  
  A quick note on what "schema" means here
&lt;/h2&gt;

&lt;p&gt;When people say "fixed schema" or "dynamic schema" in the context of A2UI, they usually mean something looser than a single JSON Schema file.&lt;/p&gt;

&lt;p&gt;In practice, the protocol is made up of separate artifacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a server-to-client envelope&lt;/li&gt;
&lt;li&gt;shared common types&lt;/li&gt;
&lt;li&gt;one or more catalogs that define renderable components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the real decision is usually this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;do you give the model a fixed catalog contract?&lt;/li&gt;
&lt;li&gt;or do you dynamically select, narrow, or extend that contract for a specific request?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the framing that matters.&lt;/p&gt;
&lt;h2&gt;
  
  
  The protocol shape you are actually building around
&lt;/h2&gt;

&lt;p&gt;For a web app, the v0.9 lifecycle is fairly straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The server sends &lt;code&gt;createSurface&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The server sends &lt;code&gt;updateComponents&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The server may send &lt;code&gt;updateDataModel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client renders the surface and sends actions back.&lt;/li&gt;
&lt;li&gt;The server continues streaming updates as needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A minimal shape looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v0.9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createSurface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"surfaceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"signup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"catalogId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://a2ui.org/specification/v0_9/basic_catalog.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sendDataModel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v0.9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updateComponents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"surfaceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"signup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Column"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"# Create account"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TextField"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/user/email"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submit_label"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Continue"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submit_label"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submitSignup"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important implementation detail is that A2UI uses a flat list of components linked by IDs.&lt;br&gt;
That is a good choice because it is easier to emit, easier to diff, and easier to validate for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate IDs&lt;/li&gt;
&lt;li&gt;missing &lt;code&gt;root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;dangling references&lt;/li&gt;
&lt;li&gt;cycles&lt;/li&gt;
&lt;li&gt;orphaned components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are exactly the boring details that determine whether a generative UI system survives contact with production.&lt;/p&gt;
&lt;h2&gt;
  
  
  Strategy 1: Fixed schema
&lt;/h2&gt;

&lt;p&gt;Fixed schema means your application chooses the catalog ahead of time and the model only emits valid instances of UI that conform to that contract.&lt;/p&gt;

&lt;p&gt;This is the default I would recommend for most teams.&lt;/p&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the workflow is business-critical&lt;/li&gt;
&lt;li&gt;the UI shape is predictable&lt;/li&gt;
&lt;li&gt;you want strong testability&lt;/li&gt;
&lt;li&gt;analytics and event handling need to stay stable&lt;/li&gt;
&lt;li&gt;you do not want renderer behavior drifting between requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;onboarding flows&lt;/li&gt;
&lt;li&gt;support triage&lt;/li&gt;
&lt;li&gt;approvals&lt;/li&gt;
&lt;li&gt;payments&lt;/li&gt;
&lt;li&gt;account management&lt;/li&gt;
&lt;li&gt;healthcare or compliance-heavy forms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not the places where you want model creativity.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why fixed schema works
&lt;/h3&gt;

&lt;p&gt;A fixed schema gives you a stable protocol boundary.&lt;br&gt;
That buys you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prompts are easier to reason about&lt;/li&gt;
&lt;li&gt;examples are easier to curate&lt;/li&gt;
&lt;li&gt;QA can build reusable fixtures&lt;/li&gt;
&lt;li&gt;regressions are easier to reproduce&lt;/li&gt;
&lt;li&gt;analytics stay more consistent&lt;/li&gt;
&lt;li&gt;frontend behavior remains predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also makes debugging much easier.&lt;br&gt;
When something breaks, you are usually debugging a bad payload, not a drifting contract and a bad payload at the same time.&lt;/p&gt;
&lt;h3&gt;
  
  
  My rule of thumb
&lt;/h3&gt;

&lt;p&gt;If the flow is revenue-critical, compliance-heavy, or operationally sensitive, start fixed.&lt;/p&gt;

&lt;p&gt;You can always loosen the system later.&lt;br&gt;
Going the other direction is usually painful.&lt;/p&gt;
&lt;h2&gt;
  
  
  Strategy 2: Dynamic schema
&lt;/h2&gt;

&lt;p&gt;Dynamic schema is where things get more interesting.&lt;br&gt;
It is also where teams get themselves into trouble.&lt;/p&gt;

&lt;p&gt;What I mean by dynamic schema is not "let the model invent whatever UI primitives it wants."&lt;/p&gt;

&lt;p&gt;That is not a schema strategy.&lt;br&gt;
That is giving up.&lt;/p&gt;

&lt;p&gt;In a production A2UI application, dynamic schema should still be renderer-owned.&lt;br&gt;
The client needs to know how to render every component the model is allowed to reference.&lt;/p&gt;

&lt;p&gt;So in practice, dynamic schema usually means one of three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dynamic catalog selection from a trusted set&lt;/li&gt;
&lt;li&gt;Dynamic use of client-advertised inline catalogs&lt;/li&gt;
&lt;li&gt;Dynamic overlays that narrow or extend a trusted base catalog for a specific request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That third option is the one I would ship most often.&lt;/p&gt;
&lt;h2&gt;
  
  
  The dynamic pattern I recommend
&lt;/h2&gt;

&lt;p&gt;Do not let the model generate a brand new universe from scratch.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start from a fixed trusted base catalog.&lt;/li&gt;
&lt;li&gt;Let the model generate a task-shaped overlay.&lt;/li&gt;
&lt;li&gt;Validate that overlay.&lt;/li&gt;
&lt;li&gt;Merge it into the base contract for prompting and validation.&lt;/li&gt;
&lt;li&gt;Fall back to the base contract if the overlay keeps failing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives you flexibility without losing control.&lt;br&gt;
Most products do not need unbounded UI generation.&lt;br&gt;
They need a safe way to adapt a known renderer contract to the long tail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzbyj9x9muo6q41e10kz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzbyj9x9muo6q41e10kz.png" alt="Animated diagram of the A2UI dynamic overlay loop showing fixed-by-default policy, overlay validation and repair, and deterministic fallback." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why validation is non-negotiable
&lt;/h2&gt;

&lt;p&gt;A2UI v0.9 is more prompt-friendly.&lt;br&gt;
It is not a security model.&lt;/p&gt;

&lt;p&gt;If you allow a model to shape any part of the UI contract, then validation becomes part of the product, not an implementation detail.&lt;/p&gt;

&lt;p&gt;At minimum, a serious pipeline should validate two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The schema contract itself&lt;/li&gt;
&lt;li&gt;The final A2UI payload generated against that contract&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That separation matters because a valid overlay does not guarantee a valid payload.&lt;/p&gt;

&lt;p&gt;For dynamic schema in particular, I would validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;overlay structure&lt;/li&gt;
&lt;li&gt;component and property compatibility&lt;/li&gt;
&lt;li&gt;child reference types&lt;/li&gt;
&lt;li&gt;topology integrity&lt;/li&gt;
&lt;li&gt;final payload instances against the selected catalog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this layer, dynamic schema quickly turns into "the backend forwards malformed intent and hopes the renderer is charitable."&lt;/p&gt;
&lt;h2&gt;
  
  
  The architecture I would actually ship
&lt;/h2&gt;

&lt;p&gt;If I were building a fullstack agent web app around A2UI, this is the high-level policy I would use.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Fixed by default
&lt;/h3&gt;

&lt;p&gt;Use a fixed catalog for the majority of flows.&lt;br&gt;
Keep the happy path boring.&lt;/p&gt;

&lt;p&gt;That is where stability and predictable UX come from.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Dynamic for the long tail
&lt;/h3&gt;

&lt;p&gt;Use dynamic overlays only for workflows that are genuinely exploratory, tenant-specific, or too variable for one static prompt contract.&lt;/p&gt;

&lt;p&gt;This keeps the flexible path available without making the whole system probabilistic.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Dynamic means task-shaped, not unbounded
&lt;/h3&gt;

&lt;p&gt;Restrict dynamic behavior to things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;narrowing allowed components&lt;/li&gt;
&lt;li&gt;adding task-specific examples or constraints&lt;/li&gt;
&lt;li&gt;selecting from trusted custom catalogs&lt;/li&gt;
&lt;li&gt;merging safe overlays over a base catalog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not let the model invent component semantics the client never negotiated.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Validate, repair, retry, fallback
&lt;/h3&gt;

&lt;p&gt;The loop should be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate candidate overlay&lt;/li&gt;
&lt;li&gt;Validate it&lt;/li&gt;
&lt;li&gt;If invalid, feed the error back to the model&lt;/li&gt;
&lt;li&gt;Retry a few times&lt;/li&gt;
&lt;li&gt;Fall back to fixed if it still fails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I would cap retries at around five attempts.&lt;/p&gt;

&lt;p&gt;Past that point, you are not repairing the schema.&lt;br&gt;
You are negotiating with entropy.&lt;/p&gt;

&lt;p&gt;At a high level, the control flow should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user request
  -&amp;gt; choose fixed or dynamic policy
  -&amp;gt; if dynamic:
       generate candidate overlay
       validate overlay
       if invalid:
         return validation error to model
         retry up to N times
       if still invalid:
         fall back to fixed catalog
  -&amp;gt; generate final A2UI payload
  -&amp;gt; validate final payload
  -&amp;gt; stream to client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation should be just as explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MAX_SCHEMA_ATTEMPTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_catalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fixed_catalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_fixed_catalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_capabilities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;should_use_dynamic_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fixed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fixed_catalog&lt;/span&gt;

    &lt;span class="n"&gt;last_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;last_overlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_SCHEMA_ATTEMPTS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm_generate_overlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;request_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;client_capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client_capabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;previous_error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;last_error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;broken_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;last_overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;selected_catalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_overlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_capabilities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selected_catalog&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;last_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;last_overlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fallback-fixed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fixed_catalog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Validate the final payload too
&lt;/h3&gt;

&lt;p&gt;Even after the schema contract passes, validate the generated A2UI payload before it reaches the client.&lt;/p&gt;

&lt;p&gt;Schema validation and instance validation are separate responsibilities.&lt;br&gt;
Treat them that way.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Cache successful overlays
&lt;/h3&gt;

&lt;p&gt;If a task-shaped overlay validates and performs well for a repeated intent, cache it by tenant, task family, and capability set.&lt;/p&gt;

&lt;p&gt;That reduces latency, prompt cost, and repeated schema thrash.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Treat all agent output as untrusted
&lt;/h3&gt;

&lt;p&gt;This should be obvious, but it is still worth stating directly.&lt;/p&gt;

&lt;p&gt;Agent messages, UI payloads, and model-shaped schema overlays are all untrusted input.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;sanitization&lt;/li&gt;
&lt;li&gt;strict renderer boundaries&lt;/li&gt;
&lt;li&gt;strong CSP and isolation where relevant&lt;/li&gt;
&lt;li&gt;no casual trust just because the output happens to be JSON-shaped&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A useful mental model
&lt;/h2&gt;

&lt;p&gt;I think the cleanest way to reason about A2UI is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the renderer is trusted&lt;/li&gt;
&lt;li&gt;the model is constrained&lt;/li&gt;
&lt;li&gt;the backend is the enforcer&lt;/li&gt;
&lt;li&gt;fallback is deterministic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the architecture.&lt;br&gt;
Once you see it that way, the fixed-versus-dynamic decision gets much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use which approach
&lt;/h2&gt;

&lt;p&gt;My practical rule is simple.&lt;/p&gt;

&lt;p&gt;Use fixed schema when you want reliability.&lt;br&gt;
Use dynamic overlays when variability is real and the value justifies the complexity.&lt;/p&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fixed for core trusted workflows&lt;/li&gt;
&lt;li&gt;dynamic for the long tail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Per surface, not per company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A2UI v0.9 is a meaningful step forward for agent-driven interfaces.&lt;/p&gt;

&lt;p&gt;The flatter structure, prompt-first posture, swappable catalogs, and explicit lifecycle make it much more usable than older "generate a giant nested object and pray" approaches.&lt;/p&gt;

&lt;p&gt;But the big lesson is not that everything should become dynamic.&lt;br&gt;
The big lesson is that agent-driven UI still needs architecture.&lt;/p&gt;

&lt;p&gt;The posture I would use is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fixed schemas for stable, high-confidence workflows&lt;/li&gt;
&lt;li&gt;dynamic overlays for the long tail&lt;/li&gt;
&lt;li&gt;strict validation in the middle&lt;/li&gt;
&lt;li&gt;repair loops for recoverable failures&lt;/li&gt;
&lt;li&gt;deterministic fallback when the model cannot produce a safe contract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives you flexibility without surrendering control.&lt;br&gt;
And in agent systems, control is the thing that lets creativity survive production.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/" rel="noopener noreferrer"&gt;A2UI homepage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/" rel="noopener noreferrer"&gt;Google Developers Blog: Introducing A2UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/specification/v0.9-a2ui/" rel="noopener noreferrer"&gt;A2UI Protocol v0.9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/specification/v0.9-evolution-guide/" rel="noopener noreferrer"&gt;A2UI v0.8.1 to v0.9 evolution guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/quickstart/" rel="noopener noreferrer"&gt;A2UI quickstart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/guides/renderer-development/" rel="noopener noreferrer"&gt;A2UI renderer development guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/guides/custom-components/" rel="noopener noreferrer"&gt;A2UI custom component catalogs guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2ui.org/specification/v0.9-a2ui/#validator-compliance-custom-catalogs" rel="noopener noreferrer"&gt;A2UI v0.9 validator compliance and custom catalogs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/google/A2UI/main/agent_sdks/python/agent_development.md" rel="noopener noreferrer"&gt;A2UI Python SDK agent development guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/google/A2UI/main/agent_sdks/python/README.md" rel="noopener noreferrer"&gt;A2UI Python SDK README&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Frontend Reward Loop for Agentic Software</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 18 Mar 2026 23:02:37 +0000</pubDate>
      <link>https://forem.com/blove/the-frontend-reward-loop-for-agentic-software-4cig</link>
      <guid>https://forem.com/blove/the-frontend-reward-loop-for-agentic-software-4cig</guid>
      <description>&lt;p&gt;I have been thinking about this after reading the rLLM work on post-training language agents.&lt;/p&gt;

&lt;p&gt;The big idea in that work is right: if agents are going to improve, they need a loop.&lt;br&gt;
Not just inference.&lt;br&gt;
A loop of action, feedback, and improvement.&lt;/p&gt;

&lt;p&gt;What I want to argue in this post is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The frontend is the best place to collect the heuristics for that loop.&lt;/li&gt;
&lt;li&gt;Most teams should push prompt augmentation much further before training an offline reward model.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agent quality is now a feedback-loop problem, not only a model-size problem.&lt;/li&gt;
&lt;li&gt;The frontend is the only place where intent, correction, and outcome are visible together.&lt;/li&gt;
&lt;li&gt;Product heuristics can drive real gains without starting with offline reward-model training.&lt;/li&gt;
&lt;li&gt;Prompt augmentation gets most teams very far on short and medium-horizon workflows.&lt;/li&gt;
&lt;li&gt;Move to offline RL only when prompt gains flatten or long-horizon credit assignment becomes the bottleneck.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fullstack Agentic Applications
&lt;/h2&gt;

&lt;p&gt;Be sure to check out the &lt;a href="https://cacheplane.ai" rel="noopener noreferrer"&gt;Angular Agent Framework&lt;/a&gt; for building fullstack agentic applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  From static intelligence to live behavior
&lt;/h2&gt;

&lt;p&gt;Reasoning models can do very well on static tasks.&lt;br&gt;
Agentic software lives in dynamic tasks.&lt;/p&gt;

&lt;p&gt;That means we care less about one-shot correctness and more about behavioral quality over a sequence of steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the system choose a sensible next action?&lt;/li&gt;
&lt;li&gt;Did the user need to undo or rewrite it?&lt;/li&gt;
&lt;li&gt;Did the workflow actually complete?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why post-training agents matter.&lt;br&gt;
And this is why product teams need to treat interaction data as first-class infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the frontend is the best reward surface
&lt;/h2&gt;

&lt;p&gt;The backend can tell us what was called.&lt;br&gt;
The model logs can tell us what token came next.&lt;br&gt;
Only the frontend can reliably tell us whether the user felt the action was useful.&lt;/p&gt;

&lt;p&gt;The frontend gives us a dense stream of behavior signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accept vs reject&lt;/li&gt;
&lt;li&gt;Minor edit vs full rewrite&lt;/li&gt;
&lt;li&gt;One-shot completion vs retry loops&lt;/li&gt;
&lt;li&gt;Continue vs abandon&lt;/li&gt;
&lt;li&gt;Agent flow vs human escalation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not perfect labels.&lt;br&gt;
They are heuristics.&lt;br&gt;
But they are high signal and available immediately.&lt;/p&gt;

&lt;p&gt;In practice, this is the best early reward surface most teams have.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical heuristic set
&lt;/h2&gt;

&lt;p&gt;If I were starting today, I would implement four buckets.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Correction heuristics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Edit distance between agent output and final submitted value&lt;/li&gt;
&lt;li&gt;Undo/revert frequency after agent actions&lt;/li&gt;
&lt;li&gt;Manual overwrite rate&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Friction heuristics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Time-to-next-meaningful-action&lt;/li&gt;
&lt;li&gt;Retry count for same intent&lt;/li&gt;
&lt;li&gt;Dead-end path frequency&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Outcome heuristics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Task completion rate&lt;/li&gt;
&lt;li&gt;Reopen/regression rate&lt;/li&gt;
&lt;li&gt;SLA-aware completion time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Trust heuristics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Delegation rate for repeat tasks&lt;/li&gt;
&lt;li&gt;Human escalation rate&lt;/li&gt;
&lt;li&gt;Opt-out after poor outcomes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No single metric is enough.&lt;br&gt;
A composite score is usually better than betting on one proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I want to challenge: do we need offline reward models first?
&lt;/h2&gt;

&lt;p&gt;Usually, no.&lt;/p&gt;

&lt;p&gt;I think most teams can get much further than expected with prompt augmentation before training a separate reward model offline.&lt;/p&gt;

&lt;p&gt;When I say prompt augmentation, I mean the full runtime strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better task framing&lt;/li&gt;
&lt;li&gt;Better context packing from recent user behavior&lt;/li&gt;
&lt;li&gt;Better tool constraints and checks&lt;/li&gt;
&lt;li&gt;Better fallback prompts for low-confidence cases&lt;/li&gt;
&lt;li&gt;Better routing between prompt variants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is still behavior optimization.&lt;br&gt;
It is just inference-time optimization.&lt;/p&gt;

&lt;p&gt;For many product workflows, that is enough to unlock most early gains.&lt;/p&gt;

&lt;h2&gt;
  
  
  How far prompt augmentation gets us
&lt;/h2&gt;

&lt;p&gt;Prompt augmentation gets us very far on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bounded workflows&lt;/li&gt;
&lt;li&gt;Clear completion criteria&lt;/li&gt;
&lt;li&gt;Frequent user corrections&lt;/li&gt;
&lt;li&gt;Fast evaluation loops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal support triage&lt;/li&gt;
&lt;li&gt;Structured drafting and form completion&lt;/li&gt;
&lt;li&gt;Actionable summarization&lt;/li&gt;
&lt;li&gt;Multi-step UI workflows with explicit completion states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where it starts to fail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long trajectories with delayed outcomes&lt;/li&gt;
&lt;li&gt;Heavy credit assignment ambiguity&lt;/li&gt;
&lt;li&gt;High cost/latency from complex inference-time strategies&lt;/li&gt;
&lt;li&gt;Persistent instability across prompt variants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is when offline trajectory training and RL become worth the investment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended rollout
&lt;/h2&gt;

&lt;p&gt;If you are building agentic product features now, this is the sequence I would use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instrument frontend interaction events with stable task/session IDs.&lt;/li&gt;
&lt;li&gt;Define a small heuristic scorecard (correction, friction, outcome, trust).&lt;/li&gt;
&lt;li&gt;Use that scorecard to drive prompt and routing updates weekly.&lt;/li&gt;
&lt;li&gt;Build an eval flywheel with holdouts and regression checks.&lt;/li&gt;
&lt;li&gt;Introduce offline reward-model training only after improvements plateau.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps teams moving quickly while preserving a path to deeper optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guardrails
&lt;/h2&gt;

&lt;p&gt;None of this works well without guardrails.&lt;/p&gt;

&lt;p&gt;At minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy-safe event collection&lt;/li&gt;
&lt;li&gt;Anti-gaming checks for proxy metrics&lt;/li&gt;
&lt;li&gt;Correctness-weighted objectives (not just speed)&lt;/li&gt;
&lt;li&gt;Human review samples to calibrate heuristic drift&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we do not add guardrails, we optimize the dashboard instead of the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The backend can train the policy.&lt;br&gt;
But the frontend is where the reward signal is born.&lt;/p&gt;

&lt;p&gt;If we want agents that improve from real human interaction, we need to treat frontend behavior as training infrastructure.&lt;/p&gt;

&lt;p&gt;And we should be honest about sequencing:&lt;br&gt;
Start with prompt augmentation.&lt;br&gt;
Push it hard.&lt;br&gt;
Then add offline RL when the data proves you need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Angular &amp;amp; LangChain together?
&lt;/h2&gt;

&lt;p&gt;Check out the &lt;a href="https://stream-resource.dev" rel="noopener noreferrer"&gt;the Enterprise Streaming Resource for LangChain and Angular&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;rLLM: &lt;a href="https://pretty-radio-b75.notion.site/rLLM-A-Framework-for-Post-Training-Language-Agents-21b81902c146819db63cd98a54ba5f31" rel="noopener noreferrer"&gt;A Framework for Post-Training Language Agents&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;rLLM Docs: &lt;a href="https://rllm-project.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;rLLM Project Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sutton and Silver: [Welcome to the Era of Experience](&lt;a href="https://storage.googleapis.com/deepmind-media/Era-of-Experience%20/The%20Era%20of%20Experience%20Paper.pdf" rel="noopener noreferrer"&gt;https://storage.googleapis.com/deepmind-media/Era-of-Experience%20/The%20Era%20of%20Experience%20Paper.pdf&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Landscape of Generative UI in 2026</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 18 Mar 2026 23:02:36 +0000</pubDate>
      <link>https://forem.com/blove/the-landscape-of-generative-ui-in-2026-ad6</link>
      <guid>https://forem.com/blove/the-landscape-of-generative-ui-in-2026-ad6</guid>
      <description>&lt;p&gt;Generative UI is no longer just "chat that can answer questions."&lt;br&gt;&lt;br&gt;
In 2026, it is a product and architecture decision.&lt;/p&gt;

&lt;p&gt;In this post, I want to give a practical framework I keep coming back to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Chat Components&lt;/li&gt;
&lt;li&gt;Component Systems&lt;/li&gt;
&lt;li&gt;Embedded Generative UI&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generative UI is a spectrum, not a single implementation pattern.&lt;/li&gt;
&lt;li&gt;Each pattern optimizes for a different balance of control and flexibility.&lt;/li&gt;
&lt;li&gt;Most teams should mix patterns by surface area, not pick one globally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this matters now
&lt;/h2&gt;

&lt;p&gt;The first wave of AI products proved that people will use chat interfaces.&lt;br&gt;
The next wave is about execution: helping users actually complete work.&lt;/p&gt;

&lt;p&gt;Text-only responses are often too slow for real workflows.&lt;br&gt;
Users need affordances to compare options, edit inputs, and take action with confidence.&lt;/p&gt;

&lt;p&gt;That is where Generative UI becomes useful: it converts intent into usable interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Generative UI is
&lt;/h2&gt;

&lt;p&gt;Generative UI is UI that is selected, composed, or embedded at runtime based on user intent and agent reasoning.&lt;/p&gt;

&lt;p&gt;It is not "markdown in a chat bubble."&lt;br&gt;
It is a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the model interprets intent&lt;/li&gt;
&lt;li&gt;tools fetch or mutate state&lt;/li&gt;
&lt;li&gt;the UI renders clear actions and state transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'm working on.
&lt;/h2&gt;

&lt;p&gt;Before we go any further, I'd love to share that I am building &lt;a href="https://cacheplane.ai" rel="noopener noreferrer"&gt;Angular Agent Framework&lt;/a&gt; at cacheplane.ai&lt;/p&gt;

&lt;h2&gt;
  
  
  The spectrum
&lt;/h2&gt;

&lt;p&gt;I think about this as &lt;strong&gt;More Control -&amp;gt; More Freedom&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Chat Components&lt;/li&gt;
&lt;li&gt;Component Systems&lt;/li&gt;
&lt;li&gt;Embedded Generative UI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are complementary patterns.&lt;br&gt;
You can (and often should) use all three in one product.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Chat Components
&lt;/h2&gt;

&lt;p&gt;Chat Components are predefined UI blocks that the agent can invoke.&lt;br&gt;
The frontend team still owns the component implementation and behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2llgogwyx4fgmbtdxuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2llgogwyx4fgmbtdxuv.png" alt="Animated diagram of Chat Components where agent messages trigger a fixed set of approved UI components." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why teams start here
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fastest path to a reliable production experience&lt;/li&gt;
&lt;li&gt;Strong control over brand and behavior&lt;/li&gt;
&lt;li&gt;Easier trust and security posture from explicit contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoffs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;New UI shapes still require frontend releases&lt;/li&gt;
&lt;li&gt;Less flexible for novel, long-tail requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best fit
&lt;/h3&gt;

&lt;p&gt;Core, high-trust, brand-critical flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Component Systems
&lt;/h2&gt;

&lt;p&gt;Component Systems use schema-driven composition.&lt;br&gt;
The model or backend provides structured payloads, and the frontend composes the screen from reusable primitives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw4enwf2trckiuqbu48s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw4enwf2trckiuqbu48s.png" alt="Animated diagram of Component Systems where schema rows stream into composed UI modules." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this pattern scales
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Lower coupling between backend behavior and frontend rendering&lt;/li&gt;
&lt;li&gt;Better coverage for long-tail UI permutations&lt;/li&gt;
&lt;li&gt;Consistent visual system even with many generated layouts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoffs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;More engineering investment up front&lt;/li&gt;
&lt;li&gt;Less pixel-perfect than handcrafted, one-off UI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best fit
&lt;/h3&gt;

&lt;p&gt;Enterprise and platform surfaces with broad variability and repeatable primitives.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Embedded Generative UI
&lt;/h2&gt;

&lt;p&gt;Embedded Generative UI is when a host app embeds external app surfaces and coordinates secure handoff between agent context and embedded execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxctym32wcpyep5okn9j0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxctym32wcpyep5okn9j0.png" alt="Animated diagram of Embedded Generative UI where a host app securely hands off context to embedded app surfaces." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why teams adopt it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maximum flexibility for specialized experiences&lt;/li&gt;
&lt;li&gt;Natural path to ecosystem or app-platform strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoffs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hardest developer experience&lt;/li&gt;
&lt;li&gt;Inconsistent presentation across embedded surfaces&lt;/li&gt;
&lt;li&gt;More complex security and permission design&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best fit
&lt;/h3&gt;

&lt;p&gt;Super-host products where extensibility is part of the core value proposition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is already being used (verified examples)
&lt;/h2&gt;

&lt;p&gt;The examples below are based on public product/docs updates and are accurate as of February 21, 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chat Components examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI's &lt;a href="https://openai.com/index/spring-update/" rel="noopener noreferrer"&gt;Spring Update (May 13, 2024)&lt;/a&gt; states ChatGPT users can "analyze data and create charts," which is a concrete case of model responses invoking visual components inside chat.&lt;/li&gt;
&lt;li&gt;Salesforce announced &lt;a href="https://investor.salesforce.com/press-releases/press-release-details/2025/Salesforce-Unveils-Agentforce-2dx-A-Major-Leap-Forward-in-Its-Digital-Labor-Platform/default.aspx" rel="noopener noreferrer"&gt;Agentforce Cards (March 5, 2025)&lt;/a&gt;, where agents embed Lightning Web Components in responses.&lt;/li&gt;
&lt;li&gt;Intercom's &lt;a href="https://www.intercom.com/changes/en/197926-new-card-design-for-fin-answers" rel="noopener noreferrer"&gt;Fin answer cards update (January 27, 2025)&lt;/a&gt; is another example of structured, reusable UI cards rendered inline in conversational responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component Systems examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Copilot Studio documents &lt;a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/adaptive-cards-overview" rel="noopener noreferrer"&gt;Adaptive Cards&lt;/a&gt; as JSON-defined custom UI, including inputs and submit actions, rendered in chat surfaces.&lt;/li&gt;
&lt;li&gt;Microsoft 365 Copilot API documents &lt;a href="https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api-plugin-adaptive-cards" rel="noopener noreferrer"&gt;Adaptive Card response templates&lt;/a&gt;, including static and dynamic templates that map cleanly to schema-driven composition.&lt;/li&gt;
&lt;li&gt;Salesforce's &lt;a href="https://developer.salesforce.com/blogs/2025/09/best-practices-for-designing-agentforce-agents-for-scale" rel="noopener noreferrer"&gt;adaptive response formats&lt;/a&gt; (for example rich choice and rich link) show a standardized response schema approach for composing predictable UI affordances.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Embedded Generative UI examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI's &lt;a href="https://developers.openai.com/apps-sdk/reference" rel="noopener noreferrer"&gt;Apps SDK reference&lt;/a&gt; describes components rendered in ChatGPT and connected through a &lt;code&gt;window.openai&lt;/code&gt; bridge, with explicit iframe and CSP controls.&lt;/li&gt;
&lt;li&gt;OpenAI's &lt;a href="https://developers.openai.com/blog/chatgpt-apps-lessons" rel="noopener noreferrer"&gt;ChatGPT apps lessons learned (February 4, 2026)&lt;/a&gt; notes they built roughly two dozen apps and discusses practical iframe architecture patterns for hosted app experiences.&lt;/li&gt;
&lt;li&gt;OpenAI's &lt;a href="https://openai.com/index/introducing-gpts/" rel="noopener noreferrer"&gt;Introducing GPTs (November 2023)&lt;/a&gt; and the &lt;a href="https://help.openai.com/en/articles/8798868-introducing-the-gpt-store" rel="noopener noreferrer"&gt;GPT Store help article&lt;/a&gt; document the host + app-store model in ChatGPT.&lt;/li&gt;
&lt;li&gt;Anthropic's &lt;a href="https://www.anthropic.com/news/introducing-artifacts" rel="noopener noreferrer"&gt;Artifacts overview&lt;/a&gt; and &lt;a href="https://support.anthropic.com/en/articles/9517075-what-are-artifacts-and-how-do-i-use-them" rel="noopener noreferrer"&gt;Artifacts support docs&lt;/a&gt; describe interactive, side-panel app-like outputs embedded directly in the Claude host experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started: Hashbrown
&lt;/h2&gt;

&lt;p&gt;If you want to implement Generative UI in a web app today with Hashbrown, the core pattern is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stream model output from your backend.&lt;/li&gt;
&lt;li&gt;Register renderable components in the frontend.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;useUiChat&lt;/code&gt; so assistant messages can include UI trees, not just text.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The official setup in Hashbrown docs/README is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;@hashbrownai/{core,react,openai}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wrap your app with &lt;code&gt;HashbrownProvider&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Define model-callable components with &lt;code&gt;exposeComponent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;useUiChat({ components: [...] })&lt;/code&gt; to render those components in assistant responses.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HashbrownProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;exposeComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useUiChat&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hashbrownai/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hashbrownai/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useUiChat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a helpful assistant that can render UI components.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;exposeComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FlightCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FlightCard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Show a flight option card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Airline name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Formatted ticket price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// render messages and call sendMessage(...)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Providers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HashbrownProvider&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/api/chat"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HashbrownProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/liveloveapp/hashbrown" rel="noopener noreferrer"&gt;Hashbrown main README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/liveloveapp/hashbrown/blob/main/packages/react/README.md" rel="noopener noreferrer"&gt;Hashbrown React README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/liveloveapp/hashbrown/blob/main/packages/react/src/hooks/use-ui-chat.tsx" rel="noopener noreferrer"&gt;Hashbrown &lt;code&gt;useUiChat&lt;/code&gt; source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started: CopilotKit (v2 APIs)
&lt;/h2&gt;

&lt;p&gt;For CopilotKit, a practical v2 path is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the root &lt;code&gt;&amp;lt;CopilotKit&amp;gt;&lt;/code&gt; provider to connect to your runtime.&lt;/li&gt;
&lt;li&gt;Use v2 chat components (for example &lt;code&gt;CopilotChat&lt;/code&gt; or &lt;code&gt;CopilotSidebar&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Register v2 frontend tools with &lt;code&gt;useFrontendTool&lt;/code&gt; and a &lt;code&gt;render&lt;/code&gt; function for in-chat UI.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CopilotKit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@copilotkit/react-core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;CopilotChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ToolCallStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useFrontendTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@copilotkit/react-core/v2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@copilotkit/react-core/v2/styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ToolUIs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useFrontendTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showFlightCard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Display a flight option card in the chat UI&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;ToolCallStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Complete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading card...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FlightCard&lt;/span&gt; &lt;span class="na"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;airline&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CopilotKit&lt;/span&gt; &lt;span class="na"&gt;runtimeUrl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/api/copilotkit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ToolUIs&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CopilotChat&lt;/span&gt; &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"travel-agent"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CopilotKit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes for v2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep hooks/components from &lt;code&gt;@copilotkit/react-core/v2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Import the provider from &lt;code&gt;@copilotkit/react-core&lt;/code&gt; (as the v2 docs specify).&lt;/li&gt;
&lt;li&gt;Use Zod schemas for v2 tool parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CopilotKit/CopilotKit/blob/main/docs/content/docs/reference/v2/index.mdx" rel="noopener noreferrer"&gt;CopilotKit v2 API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CopilotKit/CopilotKit/blob/main/docs/content/docs/reference/v2/components/CopilotChat.mdx" rel="noopener noreferrer"&gt;CopilotChat (v2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CopilotKit/CopilotKit/blob/main/docs/content/docs/reference/v2/hooks/useFrontendTool.mdx" rel="noopener noreferrer"&gt;useFrontendTool (v2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CopilotKit/CopilotKit/tree/main/examples/v2" rel="noopener noreferrer"&gt;CopilotKit v2 example app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the right pattern
&lt;/h2&gt;

&lt;p&gt;My practical rule: choose per surface, not per company.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Chat Components&lt;/strong&gt; for trusted core workflows.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Component Systems&lt;/strong&gt; for scalable, long-tail generation.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Embedded Generative UI&lt;/strong&gt; only where ecosystem value clearly outweighs complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture notes that matter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep UI contracts owned by frontend.&lt;/li&gt;
&lt;li&gt;Keep tool boundaries explicit (read, write, side effects).&lt;/li&gt;
&lt;li&gt;Keep agent behavior portable across model providers.&lt;/li&gt;
&lt;li&gt;Match security by pattern:&lt;/li&gt;
&lt;li&gt;Chat Components: strict, typed contracts.&lt;/li&gt;
&lt;li&gt;Component Systems: schema validation plus policy checks.&lt;/li&gt;
&lt;li&gt;Embedded Generative UI: sandboxing, origin boundaries, and explicit permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation progression I recommend
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Start with Chat Components for one critical workflow.&lt;/li&gt;
&lt;li&gt;Add Component Systems for breadth and speed.&lt;/li&gt;
&lt;li&gt;Add Embedded Generative UI selectively for ecosystem scenarios.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep one reference workflow (for example, flight booking) across all three stages.&lt;br&gt;
It makes tradeoffs concrete and easier to explain to stakeholders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Generative UI is not just a model capability.&lt;br&gt;
It is a design and systems decision.&lt;/p&gt;

&lt;p&gt;In 2026, the strongest strategy is intentional composition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start constrained.&lt;/li&gt;
&lt;li&gt;Expand with structure.&lt;/li&gt;
&lt;li&gt;Embed only where leverage is clear.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>OpenClaw Setup, Configuration, and Key Takeaways</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 18 Mar 2026 23:02:35 +0000</pubDate>
      <link>https://forem.com/blove/openclaw-setup-configuration-and-key-takeaways-5ce8</link>
      <guid>https://forem.com/blove/openclaw-setup-configuration-and-key-takeaways-5ce8</guid>
      <description>&lt;p&gt;I set up OpenClaw on macOS with Telegram as my primary channel. The initial setup was quick, but I hit pairing and health-check failures that caused the first message to hang. This post captures what worked, what broke, and the operating policy I now use by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;macOS desktop app + CLI&lt;/li&gt;
&lt;li&gt;Node &lt;code&gt;v22.14.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;OpenClaw &lt;code&gt;2026.2.19-2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Telegram bot channel enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Baseline setup
&lt;/h2&gt;

&lt;p&gt;I started with the gateway and channel checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw gateway probe
openclaw status &lt;span class="nt"&gt;--deep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gateway reachable on &lt;code&gt;ws://127.0.0.1:18789&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Telegram configured and healthy&lt;/li&gt;
&lt;li&gt;Agent session available&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Pairing the macOS app
&lt;/h2&gt;

&lt;p&gt;My first blocker was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gateway closed (1008): pairing required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I inspected pending and paired devices and approved the app request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw devices list &lt;span class="nt"&gt;--json&lt;/span&gt;
openclaw devices approve &amp;lt;request-id&amp;gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensured the macOS app had the expected auth state and roles.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Debugging "health check failed" and hanging sends
&lt;/h2&gt;

&lt;p&gt;After pairing, the app still showed a failed health check and initial sends hung. The logs showed repeated health RPC failures tied to role auth behavior, which degraded responsiveness under load.&lt;/p&gt;

&lt;p&gt;What helped most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comparing app behavior with app process stopped vs running&lt;/li&gt;
&lt;li&gt;Watching gateway logs during connection attempts&lt;/li&gt;
&lt;li&gt;Validating roles/tokens in device state before changing anything else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key lesson: trust the logs over UI symptoms.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Stabilizing and re-validating
&lt;/h2&gt;

&lt;p&gt;After correcting pairing/auth behavior, I re-ran validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw gateway restart
openclaw gateway probe
openclaw status &lt;span class="nt"&gt;--deep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final checks I care about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gateway RPC succeeds consistently&lt;/li&gt;
&lt;li&gt;Telegram status is OK&lt;/li&gt;
&lt;li&gt;No recurring pairing-upgrade loop&lt;/li&gt;
&lt;li&gt;No repeating unauthorized health errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Default security policy (what OpenClaw should always/never do)
&lt;/h2&gt;

&lt;p&gt;This is the policy I now use as my default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Always
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ask before risky actions; state what/why first.&lt;/li&gt;
&lt;li&gt;Show exact files/commands changed.&lt;/li&gt;
&lt;li&gt;Run relevant tests/lint before declaring done.&lt;/li&gt;
&lt;li&gt;Redact secrets/tokens from output.&lt;/li&gt;
&lt;li&gt;Ask clarifying questions when requirements are ambiguous.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Never (without explicit approval)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Destructive ops (&lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, DB deletes, force-push).&lt;/li&gt;
&lt;li&gt;Sending external messages (Telegram/email/webhooks) on my behalf.&lt;/li&gt;
&lt;li&gt;Installing/updating system tools, packages, or services.&lt;/li&gt;
&lt;li&gt;Accessing or exporting secrets/credentials.&lt;/li&gt;
&lt;li&gt;Spending money or invoking paid APIs/services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pairing roles must match real app behavior, not just initial connect success.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openclaw devices list --json&lt;/code&gt; is the fastest way to spot role mismatches.&lt;/li&gt;
&lt;li&gt;Health checks are a real signal; if they fail, message send paths can stall.&lt;/li&gt;
&lt;li&gt;Re-verify after every change with &lt;code&gt;gateway probe&lt;/code&gt; and &lt;code&gt;status --deep&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Strong boundaries reduce accidental risk while still keeping automation useful.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I'm working on.
&lt;/h2&gt;

&lt;p&gt;With openclaw I am working for &lt;a href="https://cacheplane.ai" rel="noopener noreferrer"&gt;cacheplane building the Angular Agent Framework&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tooling</category>
      <category>devops</category>
    </item>
    <item>
      <title>Building Web Applications We Can Trust - The Imperative of SRE</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Sun, 11 Jun 2023 03:00:14 +0000</pubDate>
      <link>https://forem.com/blove/building-web-applications-we-can-trust-the-imperative-of-sre-3ohk</link>
      <guid>https://forem.com/blove/building-web-applications-we-can-trust-the-imperative-of-sre-3ohk</guid>
      <description>&lt;h1&gt;
  
  
  Building Web Applications We Can Trust - The Imperative of SRE
&lt;/h1&gt;

&lt;p&gt;In this article, we explore the critical role of Site Reliability Engineering (SRE) for different types of SaaS owners and web developers, ranging from those overlooking reliability issues, those fighting to escape 'reliability hell', and those aware of the importance but lacking the resources to implement it.&lt;br&gt;
We emphasize the necessity of SRE not just as a problem-solving method but as a culture, fostering a more reliable, trustworthy digital future.&lt;br&gt;
We also introduce &lt;a href="https://getpolaris.ai" rel="noopener noreferrer"&gt;Polaris, an AI-powered tool designed to support and simplify the journey towards improved reliability&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In today's digital era, web application and Software-as-a-Service (SaaS) platforms dominate our interactions, dictating how we work, communicate, shop, and more.&lt;br&gt;
As a result, reliability and trust have emerged as the most crucial features of these platforms.&lt;br&gt;
In the throes of digital transformation, if there is one thing that every SaaS owner or web developer must focus on, it's Site Reliability Engineering (SRE).&lt;br&gt;
Why? Let's explore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unresolved Reliability: Time Bomb or Opportunity?
&lt;/h2&gt;

&lt;p&gt;The first group of SaaS owners and software engineers are those who are aware of reliability issues but opt to overlook them.&lt;br&gt;
Their reasons may vary, from cost implications to rapid growth that seems to dwarf the need for reliability.&lt;br&gt;
This can be a deceptive comfort zone, the mirage of a thriving business masking an impending reliability catastrophe.&lt;br&gt;
Yes, you might have strong consumer lock-in, and your business might be on a steep growth curve.&lt;br&gt;
However, remember this: trust, once lost, is hard to regain.&lt;/p&gt;

&lt;p&gt;Consider what's at stake: your reputation, customer loyalty, and ultimately, your bottom line.&lt;br&gt;
The need for reliability isn't a business choice —- it's a business imperative.&lt;br&gt;
Don't wait for the time bomb to detonate; use the ticking to turn it into an opportunity.&lt;br&gt;
By implementing SRE best practices now, you can preemptively address potential reliability issues and strengthen user trust, providing you with a competitive edge in the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Long Journey Out of Reliability Hell: A Crucible for Change
&lt;/h2&gt;

&lt;p&gt;The second group comprises SaaS owners and engineers painfully aware that they are in what we call "reliability hell".&lt;br&gt;
You may be experiencing high churn rates, customer complaints, and even plummeting profits.&lt;br&gt;
Getting out of reliability hell is indeed a Herculean task.&lt;br&gt;
It takes time, effort, and significant investment in SRE best practices, tools, and processes.&lt;br&gt;
Yet, it's a task worth undertaking, a crucible that can forge a more resilient and trusted business.&lt;/p&gt;

&lt;p&gt;By embracing SRE, you are not just rectifying issues, but more importantly, you're establishing a culture of reliability within your organization.&lt;br&gt;
This culture is a game-changer.&lt;br&gt;
It ensures that reliability is built into the DNA of your applications, not bolted on as an afterthought.&lt;br&gt;
It fosters an environment where every code, every deployment, every update is geared towards enhancing reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledging the Need for SRE: A Call to Arms
&lt;/h2&gt;

&lt;p&gt;Lastly, there are those SaaS owners and developers who are acutely aware of the need for reliability but find themselves unprepared to take it on.&lt;br&gt;
The challenges of knowledge, skills, and resources can indeed be daunting.&lt;br&gt;
But remember this: admitting the problem is the first step to the solution.&lt;/p&gt;

&lt;p&gt;Fortunately, you're not alone in this journey.&lt;br&gt;
Tools like Polaris, an AI-powered site reliability tool, can be invaluable.&lt;br&gt;
Polaris is designed to detect outages and incidents in your web application in real time, helping you to navigate the complexities of SRE, even with limited resources.&lt;/p&gt;

&lt;p&gt;Our vision for Polaris is simple: &lt;strong&gt;to help you build applications that people trust&lt;/strong&gt;.&lt;br&gt;
Because in a world reliant on digital, trust and reliability are the true currencies of success.&lt;/p&gt;

&lt;p&gt;If you're ready to steer your web application or SaaS business towards a more reliable, more trusted future, we invite you to explore Polaris.&lt;br&gt;
Polaris is currently in beta and we'd love for you to be part of our journey.&lt;br&gt;
Feel free to sign up for Polaris, or contact us to learn more.&lt;/p&gt;

&lt;p&gt;Together, we can build a world of reliable and trusted web applications.&lt;/p&gt;

</description>
      <category>sre</category>
      <category>saas</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Embracing Site Reliability Engineering</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 10 May 2023 03:38:41 +0000</pubDate>
      <link>https://forem.com/blove/embracing-site-reliability-engineering-17ll</link>
      <guid>https://forem.com/blove/embracing-site-reliability-engineering-17ll</guid>
      <description>&lt;p&gt;Site Reliability Engineering (SRE) is transforming the way software teams approach reliability and performance in modern web applications.&lt;br&gt;
With an increasing number of frontend-focused engineers, it's essential to understand the fundamentals of SRE and implement a culture of reliability within your team and organization.&lt;br&gt;
This blog post will introduce you to the basics of Site Reliability Engineering, its importance for software engineers and managers, and provide some best practices to kickstart your journey towards a more reliable web application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Site Reliability Engineering?
&lt;/h2&gt;

&lt;p&gt;Site Reliability Engineering is a discipline that combines aspects of software engineering and operations to build and maintain scalable and reliable systems.&lt;br&gt;
Pioneered by Google, SRE focuses on automating infrastructure management, ensuring service reliability, and optimizing performance.&lt;br&gt;
SRE emphasizes the use of Service Level Objectives (SLOs) and Error Budgets as a means to balance feature development, system stability, and user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should Software Engineers and Managers Be Concerned with Reliability?
&lt;/h2&gt;

&lt;p&gt;Reliability is crucial for the success of any web application.&lt;br&gt;
A reliable application ensures a positive user experience, leading to higher user retention, satisfaction, and ultimately, revenue.&lt;br&gt;
As a software engineer or manager, it's your responsibility to ensure that your application is stable, performant, and consistently meeting the needs of your users.&lt;br&gt;
Embracing SRE principles can help you identify and address performance bottlenecks and system failures, reducing downtime and improving overall system resilience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Building a Culture of Reliability
&lt;/h2&gt;

&lt;p&gt;First, &lt;strong&gt;define clear Service Level Indicators (SLIs) and Service Level Objectives (SLOs)&lt;/strong&gt;. SLIs are metrics that quantify your system's performance, while SLOs are target values for those metrics.&lt;br&gt;
Establishing clear SLIs and SLOs helps your team set realistic goals for system reliability and provides a measurable way to track progress.&lt;br&gt;
Ensure that these metrics are aligned with your users' expectations to maintain a high-quality user experience.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;implement monitoring and observability&lt;/strong&gt;.&lt;br&gt;
Monitoring and observability are crucial for understanding your system's health and performance.&lt;br&gt;
Invest in tools that provide real-time insights into your application's performance and enable you to identify potential issues quickly.&lt;br&gt;
Polaris, an AI-powered site reliability platform, is an excellent example of a solution that helps you effectively monitor and gain observability into your web applications' performance and reliability.&lt;/p&gt;

&lt;p&gt;Third, &lt;strong&gt;automate incident response and management&lt;/strong&gt;.&lt;br&gt;
Automate processes for detecting, triaging, and resolving incidents to minimize the impact on your users.&lt;br&gt;
Integrating your monitoring tools with incident management solutions, such as PagerDuty or Jira, can help streamline the process and reduce time to resolution.&lt;/p&gt;

&lt;p&gt;Fourth, &lt;strong&gt;embrace a blameless culture&lt;/strong&gt;.&lt;br&gt;
Encourage a culture of learning and continuous improvement by treating failures as opportunities to learn rather than assigning blame.&lt;br&gt;
Conduct blameless postmortems to identify the root causes of incidents and implement preventive measures to avoid recurrence.&lt;/p&gt;

&lt;p&gt;Fifth, &lt;strong&gt;continuously iterate and improve&lt;/strong&gt;.&lt;br&gt;
Continuously evaluate and refine your processes, tools, and infrastructure.&lt;br&gt;
Keep your team informed about best practices, and encourage open communication and collaboration to foster a culture of learning and improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Site Reliability Engineering is a powerful approach to ensuring the reliability and performance of your web applications.&lt;br&gt;
By understanding its fundamentals and adopting SRE best practices, you can build a culture of reliability within your team and organization, ultimately leading to more satisfied users and a successful web application.&lt;br&gt;
Start your journey towards a more reliable web application today by embracing the principles of Site Reliability Engineering and leveraging the power of tools like Polaris to gain observability and insights into your system's performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polaris
&lt;/h2&gt;

&lt;p&gt;Building a culture of site reliability is critical to the success of an organization, especially if you are a SaaS product. But, it can be hard to climb out of reliability hell.&lt;/p&gt;

&lt;p&gt;My team is building an &lt;a href="https://getpolaris.ai" rel="noopener noreferrer"&gt;AI-powered Site Reliability Platform&lt;/a&gt; that helps SaaS companies build apps people trust by increasing the reliability and performance of your web apps. We're currently in a closed-beta. However, don't fret, you can sign up for our beta and help us build a platform that changes the way we think about site reliability.&lt;/p&gt;

</description>
      <category>sre</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Typed Forms in Angular</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Thu, 20 Oct 2022 18:47:47 +0000</pubDate>
      <link>https://forem.com/blove/typed-forms-in-angular-334c</link>
      <guid>https://forem.com/blove/typed-forms-in-angular-334c</guid>
      <description>&lt;p&gt;Angular version 14 is a feature-packed release that brings new APIs, functionality, and a developer experience.&lt;br&gt;
This includes one of the most requested feature improvements in Angular: typed forms.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to migrate to the new strongly-typed forms API in Angular version 14&lt;/li&gt;
&lt;li&gt;Typed a &lt;code&gt;FormControl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Typed a &lt;code&gt;FormGroup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Typed a &lt;code&gt;FormArray&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;New &lt;code&gt;FormRecord&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mixing typed and untyped controls&lt;/li&gt;
&lt;li&gt;Non-nullable controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;
&lt;h2&gt;
  
  
  Typed Forms API FTW 🎉
&lt;/h2&gt;

&lt;p&gt;If you have been using Angular's forms API, whether template-based or reactive forms, you likely have noticed that prior to Angular 14 much of the API surface had a &lt;em&gt;lot&lt;/em&gt; of &lt;code&gt;any&lt;/code&gt; types.&lt;/p&gt;

&lt;p&gt;Angular (version 2, not AngularJS) was released in September of 2016 (after Flash died but before build tools took the world by storm). Not long after the release, developers started to note that the API was both not type-safe (or null-safe).&lt;br&gt;
In fact, &lt;a href="https://github.com/angular/angular/issues/13721" rel="noopener noreferrer"&gt;the most upvoted and commented on issue&lt;/a&gt; in the GitHub repository was created December of 2016 to report this problem.&lt;/p&gt;

&lt;p&gt;Angular 14 introduces reactive form APIs that are both type-safe and null-safe. This is a big win for using reactive forms in Angular.&lt;br&gt;
Unfortunately, the template-driven forms API is currently not strongly typed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Migrating to Angular 14
&lt;/h2&gt;

&lt;p&gt;To get started, you need to update your project to use Angular 14 to take advantage of the new APIs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng update @angular/cli @angular/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your project is updated, you'll not that instance of &lt;code&gt;FormControl&lt;/code&gt; and &lt;code&gt;FormGroup&lt;/code&gt; have been migrated to the untyped classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FormControl&lt;/code&gt; is migrated to &lt;code&gt;UntypedFormControl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FormGroup&lt;/code&gt; is migrated to &lt;code&gt;UntypedFormGroup&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Typed &lt;code&gt;FormControl&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In Angular 14, the &lt;code&gt;FormControl&lt;/code&gt; class now has a TypeScript generic type of &lt;code&gt;TValue&lt;/code&gt; whose default type assignment is &lt;code&gt;any&lt;/code&gt;. What does this mean?&lt;/p&gt;

&lt;p&gt;Prior to Angular 14, when accessing properties such as &lt;code&gt;value&lt;/code&gt; the type was &lt;code&gt;any&lt;/code&gt;, and when using the method &lt;code&gt;setValue&lt;/code&gt;, it would accept &lt;code&gt;any&lt;/code&gt; argument value.&lt;/p&gt;

&lt;p&gt;Here are just a few of the class members that have been updated to use the &lt;code&gt;TValue&lt;/code&gt; generic type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;setValue(value: TValue)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;patchValue(value: TValue)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reset(formState: TValue)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getRawValue(): TValue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value: TValue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a big win for preventing runtime exceptions and regressions due to type errors!&lt;/p&gt;

&lt;p&gt;It's important to note that the &lt;code&gt;TValue&lt;/code&gt; generic type is inferred by TypeScript when newing-up a new &lt;code&gt;FormControl&lt;/code&gt; instance, so it's not necessary to specify the generic type.&lt;br&gt;
Let's look at an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, we did not specify the &lt;code&gt;TValue&lt;/code&gt; generic type above.&lt;br&gt;
Rather, the type is inferred by the &lt;code&gt;value&lt;/code&gt; argument specified to constructor function when creating a new instance of the &lt;code&gt;FormControl&lt;/code&gt; class.&lt;/p&gt;
&lt;h2&gt;
  
  
  Typed &lt;code&gt;FormGroup&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The purpose of Angular's &lt;code&gt;FormGroup&lt;/code&gt; is to group form controls logically in order to track their state (value, validity, etc.) together.&lt;br&gt;
Angular 14 also introduces updated typings for the &lt;code&gt;FormGroup&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;Let's look at an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SignUpForm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signUpFormGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SignUpForm&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's quickly review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we define a new &lt;code&gt;SignUpForm&lt;/code&gt; interface.&lt;/li&gt;
&lt;li&gt;The interface is &lt;em&gt;only necessary if we have optional form controls&lt;/em&gt;. In this example, the &lt;code&gt;subscribe&lt;/code&gt; control can be dynamically added and removed from the group.&lt;/li&gt;
&lt;li&gt;Next, we create a new &lt;code&gt;FormGroup&lt;/code&gt; instance, and since we have an optional control, we specify the generic type to the &lt;code&gt;SignUpForm&lt;/code&gt; interface.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;name&lt;/code&gt; control's value is typed: &lt;code&gt;string | null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;email&lt;/code&gt; control's value is typed: &lt;code&gt;string | null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;subscribe&lt;/code&gt; control value is typed: &lt;code&gt;string | null | undefined&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;signUpFormGroup&lt;/code&gt; value is typed: &lt;code&gt;Partial&amp;lt;{ name: string | null; email: string | null; subscribe: string | null | undefined }&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may have noticed that the control values can be &lt;code&gt;null&lt;/code&gt; or, if optional, &lt;code&gt;undefined&lt;/code&gt;.&lt;br&gt;
This is because Angular's form APIs allow for null values.&lt;br&gt;
For example, if we invoke the &lt;code&gt;reset()&lt;/code&gt; method on a control, the value is not set to the original/seed value, rather, it is set to &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
We'll show how to override this behavior below.&lt;/p&gt;

&lt;p&gt;It should also be noted that the &lt;code&gt;TValue&lt;/code&gt; for each control is also inferred as we previously learned.&lt;/p&gt;
&lt;h2&gt;
  
  
  Typed &lt;code&gt;FormArray&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Currently, TypeScript arrays are homogeneous.&lt;br&gt;
This means that TypeScript expects that every item in an array is of the same type (including &lt;code&gt;any&lt;/code&gt;).&lt;br&gt;
For the sake of clarity, this is not to be confused with tuples which can have unambiguous types.&lt;/p&gt;

&lt;p&gt;With that said, typed &lt;code&gt;FormArray&lt;/code&gt; instances require that all controls are homogeneously typed.&lt;br&gt;
If a &lt;code&gt;FormArray&lt;/code&gt; requires heterogeneous types then the use of the &lt;code&gt;UntypedFormArray&lt;/code&gt; is recommended.&lt;/p&gt;

&lt;p&gt;In this example we'll create a new &lt;code&gt;FormArray&lt;/code&gt; with an initial control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we have defined the &lt;code&gt;FormArray&lt;/code&gt; to include a control whose &lt;code&gt;TValue&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;, the type of the array is inferred as: &lt;code&gt;FormArray&amp;lt;AbstractControl&amp;lt;string, string&amp;gt;&amp;gt;&lt;/code&gt;.&lt;br&gt;
The compiler now expects that each item in the array is a control whose &lt;code&gt;TValue&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we attempt to add a new control to the array whose &lt;code&gt;TValue&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; a string, the compiler will throw an exception indicating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler exception indicates that there is an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Argument of type 'FormControl&amp;lt;number&amp;gt;' is not assignable to parameter of type 'AbstractControl&amp;lt;string, string&amp;gt;'.
Types of property 'value' are incompatible.
Type 'number' is not assignable to type 'string'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a good thing! 😌&lt;/p&gt;

&lt;p&gt;Ok, but what if our array of controls is initially empty?&lt;br&gt;
In that case, we can specify the generic type of the controls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormArray&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AbstractControl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we use the &lt;code&gt;AbstractControl&lt;/code&gt; type and specify the &lt;code&gt;TValue&lt;/code&gt; to be of type &lt;code&gt;string&lt;/code&gt; as we expect every control's value to be a &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  New &lt;code&gt;FormRecord&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;As we learned previously the &lt;code&gt;FormGroup&lt;/code&gt; in Angular 14+ supports specifying a group of controls whose &lt;code&gt;TValue&lt;/code&gt; types are known when creating the group, even when creating optional controls within the group.&lt;br&gt;
However, what about a group of controls whose &lt;code&gt;TValue&lt;/code&gt; is not known when creating the group.&lt;br&gt;
To meet this use case, Angular 14 ships with a new &lt;code&gt;FormRecord&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Let's look at an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AbstractControl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have created the group of controls using the &lt;code&gt;FormRecord&lt;/code&gt; class, we can start to add (and/or remove) controls from the group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;street&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code works as expected.&lt;br&gt;
A new control is added to the group that meets the type definition.&lt;br&gt;
However, what happens if we attempt to add a new control whose &lt;code&gt;TValue&lt;/code&gt; is a &lt;code&gt;number&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the compiler throws an exception indicating that we cannot add this control to the group due to the type constraints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Argument of type 'FormControl&amp;lt;number&amp;gt;' is not assignable to parameter of type 'AbstractControl&amp;lt;string, string&amp;gt;'.
Types of property 'value' are incompatible.
Type 'number' is not assignable to type 'string'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;UntypedFormGroup&lt;/code&gt; class supports the user requirements in the event that we need a group of controls whose values are heterogeneous.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixed Types
&lt;/h2&gt;

&lt;p&gt;We can declare mixed types of controls when working with a group of controls of heterogeneous types as long as we declare the controls when creating the group.&lt;br&gt;
Further, we can use the &lt;code&gt;UntypedFormControl&lt;/code&gt; class to declare a control whose &lt;code&gt;TValue&lt;/code&gt; type is &lt;code&gt;any&lt;/code&gt; (under the hood, this is a type whose generic type is preset to &lt;code&gt;any&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's look at an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;formGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;no&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UntypedFormControl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;street&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;street&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postalCode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postalCode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ABC123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This all works as we expect.&lt;br&gt;
We have a few controls, each with their own &lt;code&gt;TValue&lt;/code&gt;, including the untyped control whose &lt;code&gt;TValue&lt;/code&gt; is &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Non-nullable Controls
&lt;/h2&gt;

&lt;p&gt;If you recall from earlier, we mentioned that resetting a form control state will set the value to &lt;code&gt;null&lt;/code&gt;, not the initial/seed value as we might expect.&lt;br&gt;
With non-nullable form controls, we can explicitly instruct the compiler that the value is reset to the initial value when invoking the &lt;code&gt;reset()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Let's look at an example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;formGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nonNullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;street&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;street&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;street&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;street&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// empty string, NOT null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;There are a few key takeaways when using Angular 14's updated forms API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;FormControl&lt;/code&gt;, &lt;code&gt;FormGroup&lt;/code&gt;, and &lt;code&gt;FormArray&lt;/code&gt; signatures have been updated to support TypeScript generics.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;TValue&lt;/code&gt; type is inferred where possible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FormGroup&lt;/code&gt; supports enumerated, homogeneous, and optional controls.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FormRecord&lt;/code&gt; supports non-enumerated (dynamic) and homogenous controls.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UntypedFormControl&lt;/code&gt; is a non strongly-typed &lt;code&gt;FormControl&lt;/code&gt;. Or, in other words, the &lt;code&gt;TValue&lt;/code&gt; type is preset to &lt;code&gt;any&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UntypedFormGroup&lt;/code&gt; is a non strongly-typed &lt;code&gt;FormGroup&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FormArray&lt;/code&gt; supports homogeneously typed controls.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UntypedFormArray&lt;/code&gt; is a non strongly-typed &lt;code&gt;FormArray&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;nonNullable&lt;/code&gt; supports resetting a control state to the initial/seed value.&lt;/li&gt;
&lt;li&gt;Through strict types and template type checking, we can avoid runtime exceptions and reduce regressions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Need Help?
&lt;/h2&gt;

&lt;p&gt;Do you have questions? We can help! &lt;a href="https://liveloveapp.com/services/angular-development" rel="noopener noreferrer"&gt;LiveLoveApp provides angular consulting&lt;/a&gt;. We can answer your questions, perform an architecture review of your application and help you to implement best practices to improve your software development performance.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React Hooks with RxJS and Axios</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 24 Aug 2022 21:14:42 +0000</pubDate>
      <link>https://forem.com/blove/react-hooks-with-rxjs-and-axios-d0a</link>
      <guid>https://forem.com/blove/react-hooks-with-rxjs-and-axios-d0a</guid>
      <description>&lt;p&gt;Reactive Extensions for JavaScript, or RxJS, is a library that has a twofold purpose.&lt;br&gt;
It creates an &lt;code&gt;Observable&lt;/code&gt; primitive that is either synchronous or asynchronous, and it includes a rich library of functions that can be used to create observables, transform, filter, join, and multicast observables, provides error handling, and more.&lt;/p&gt;

&lt;p&gt;If that sounds like a lot - it is.&lt;/p&gt;

&lt;p&gt;While RxJS is commonly used in Angular projects due to the fact that it is a peer dependency, it can be overlooked by software engineers building applications using React - or other frontend JavaScript frameworks for that matter.&lt;/p&gt;

&lt;p&gt;Let me be clear - you do not &lt;em&gt;need&lt;/em&gt; to use RxJS with React.&lt;/p&gt;

&lt;p&gt;Promises, the &lt;code&gt;useEffect()&lt;/code&gt; hook, and libraries such as Axios provide much of what a typical React application requires for asynchronicity and fetching data.&lt;br&gt;
What RxJS with React &lt;em&gt;does&lt;/em&gt; provide is the ability to write pure functions for event streams, effectively handle errors within a stream of data, and easily fetch data using the native Fetch and WebSocket APIs.&lt;/p&gt;

&lt;p&gt;In this article, I'd like to share how we use &lt;a href="https://liveloveapp.com/blog/2022-07-20-react-using-rxjs-with-axios" rel="noopener noreferrer"&gt;RxJS with React&lt;/a&gt; at LiveLoveApp for rapidly developing prototypes and applications for our clients.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using &lt;code&gt;fromFetch()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One advantage to using RxJS is the provided &lt;code&gt;fromFetch()&lt;/code&gt; function that uses the native Fetch API with a cancellable &lt;code&gt;AbortController&lt;/code&gt; signal.&lt;/p&gt;

&lt;p&gt;Let's look at how you might use Axios for cancellation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://reqres.in/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error fetching user`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleOnCancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleOnCancel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's quickly review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we create a new instance of the &lt;code&gt;AbortController&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Then, as a side effect, we use Axios' &lt;code&gt;get()&lt;/code&gt; method to fetch a user from the API, providing the &lt;code&gt;AbortController&lt;/code&gt;'s signal.&lt;/li&gt;
&lt;li&gt;Finally, in the &lt;code&gt;handleOnCancel()&lt;/code&gt; callback function we invoke the &lt;code&gt;abort()&lt;/code&gt; method on the &lt;code&gt;AbortController&lt;/code&gt; instance to cancel the fetch request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When using RxJS's &lt;code&gt;fromFetch()&lt;/code&gt; function it is not necessary to wire up an &lt;code&gt;AbortController&lt;/code&gt; signal.&lt;br&gt;
Rather, we can cancel the fetch request by emitting either an error or completion notification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fromFetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs/fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;takeUntil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cancel$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fromFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://reqres.in/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nf"&gt;concatMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nf"&gt;takeUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cancel$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleOnCancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cancel$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleOnCancel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we use the &lt;code&gt;fromFetch()&lt;/code&gt; function from RxJS to use the native Fetch API to request a user. This function returns an Observable, that when subscribed to, will initiate the request.&lt;/li&gt;
&lt;li&gt;Within the &lt;code&gt;pipe()&lt;/code&gt; method, we first check if the response failed, and if so, we emit an error notification of the response's &lt;code&gt;statusText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Next, using the &lt;code&gt;concatMap()&lt;/code&gt; operator, we merge the next notification that is emitted from the Observable created internally from the Promise returned from the &lt;code&gt;.json()&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Next, we use the &lt;code&gt;takeUntil()&lt;/code&gt; operator to notify the outer Observable to complete, and abort the request if necessary, when the &lt;code&gt;cancel$&lt;/code&gt; subject emits a next notification.&lt;/li&gt;
&lt;li&gt;Finally, within the &lt;code&gt;handleOnCancel()&lt;/code&gt; callback function we invoke the &lt;code&gt;next()&lt;/code&gt; notification on the &lt;code&gt;cancel$&lt;/code&gt; Subject.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key takeaways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RxJS provides functions for interfacing with the native Fetch and WebSocket APIs using asynchronous Observables.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;fromFetch()&lt;/code&gt; operator uses the &lt;code&gt;AbortController&lt;/code&gt; internally and cancels the request if the Observable either completes or an error notification is emitted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do I handle subscriptions?
&lt;/h2&gt;

&lt;p&gt;It's best to clean up any subscriptions in our application when using RxJS.&lt;br&gt;
While there are a few different approaches to ensuring an Observable that is subscribed to is completed (or unsubscribed from), one method is to invoke the &lt;code&gt;.unsubscribe()&lt;/code&gt; method on the &lt;code&gt;Subscription&lt;/code&gt; instance that is returned from the &lt;code&gt;subscribe()&lt;/code&gt; function.&lt;br&gt;
The teardown function returned from the &lt;code&gt;useEffect()&lt;/code&gt; hook is our opportunity to perform any cleanup from the side effect.&lt;/p&gt;
&lt;h2&gt;
  
  
  De-bouncing an input stream
&lt;/h2&gt;

&lt;p&gt;In this example, we will manage a &lt;code&gt;search$&lt;/code&gt; Observable stream that is denounced before we invoke the &lt;code&gt;onSearch()&lt;/code&gt; callback function that is prop to the component.&lt;br&gt;
While we could simply invoke the &lt;code&gt;onSearch()&lt;/code&gt; callback function on each change to the input value, we want to avoid excessive network requests and repaints in the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CancelIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/icons-material/Cancel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SearchIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/icons-material/Search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;IconButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onSearch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;search$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;search$&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
        &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IconButton&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CancelIcon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchIcon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have defined a &lt;code&gt;search$&lt;/code&gt; BehaviorSubject with an initial seed value of an empty string.&lt;/li&gt;
&lt;li&gt;When the &lt;code&gt;search&lt;/code&gt; state changes the &lt;code&gt;next()&lt;/code&gt; method is invoked on the &lt;code&gt;search$&lt;/code&gt; subject with the current value.&lt;/li&gt;
&lt;li&gt;We subscribe to the &lt;code&gt;search$&lt;/code&gt; Observable stream and use the &lt;code&gt;debounceTime()&lt;/code&gt; operator to debounce the value changes of the search &lt;code&gt;HTMLInputElement&lt;/code&gt;. Within the &lt;code&gt;useEffect()&lt;/code&gt; hook we return the teardown callback function that will invoke the &lt;code&gt;unsubscribe()&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This implementation highlights the use of RxJS to manage a stream of data within our application from the &lt;code&gt;onChange&lt;/code&gt; event that is caused by the user interacting with a search input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;useRxEffect()&lt;/code&gt; Hook
&lt;/h2&gt;

&lt;p&gt;Finally, I'd like to share a simple hook that LiveLoveApp uses for our React applications that depend on RxJS.&lt;br&gt;
This hook makes it easy to not worry about subscriptions.&lt;/p&gt;

&lt;p&gt;Let's take a look.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useRxEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useRxEffect()&lt;/code&gt; hooks is intentionally similar to the &lt;code&gt;useEffect()&lt;/code&gt; hook provided by React.&lt;br&gt;
The hook expects the &lt;code&gt;factory&lt;/code&gt; function to return an &lt;code&gt;Observable&lt;/code&gt; that is unsubscribed when the effect teardown callback function is invoked.&lt;/p&gt;

&lt;p&gt;Here is a snippet of using the &lt;code&gt;useRxEffect()&lt;/code&gt; hook based on the previous code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CancelIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/icons-material/Cancel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SearchIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/icons-material/Search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;IconButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tap&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onSearch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;search$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nf"&gt;useRxEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
        &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IconButton&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CancelIcon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;search$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchIcon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example code above, note that we have replaced the &lt;code&gt;useEffect()&lt;/code&gt; hook with our custom &lt;code&gt;useRxEffect()&lt;/code&gt; hook to manage the subscribing and unsubscribing from the &lt;code&gt;search$&lt;/code&gt; Observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;If you're considering using RxJS in an existing or new React application, here are some key takeaways based on our experience:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;RxJS is not necessary to build robust React application.&lt;/li&gt;
&lt;li&gt;RxJS provides a functional programming implementation for building React applications with event streams, asynchronous data, and more.&lt;/li&gt;
&lt;li&gt;RxJS implements the Observable primitive that is compatible to Promises (but without async/await).&lt;/li&gt;
&lt;li&gt;RxJS has a rich library of functions for creating Observables, data transformation and multicasting, handling errors, and more.&lt;/li&gt;
&lt;li&gt;You can think of RxJS as lodash for events.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>rxjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AG Grid Cell Rendering Pipeline with TypeScript</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 27 Jul 2022 16:05:00 +0000</pubDate>
      <link>https://forem.com/blove/ag-grid-cell-rendering-pipeline-with-typescript-c5c</link>
      <guid>https://forem.com/blove/ag-grid-cell-rendering-pipeline-with-typescript-c5c</guid>
      <description>&lt;p&gt;Here at LiveLoveApp, we’re big fans of AG Grid - the best JavaScript grid in the world. In fact, we offer &lt;a href="https://liveloveapp.com/services/ag-grid" rel="noopener noreferrer"&gt;AG Grid implementation services&lt;/a&gt; based on our expertise!&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;For two primary reasons: performance and extensibility.&lt;br&gt;
Many of our clients use AG Grid to meet customer requirements for displaying tabular data.&lt;/p&gt;

&lt;p&gt;In this article you’ll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The AG Grid cell rendering pipeline&lt;/li&gt;
&lt;li&gt;How to leverage the new TypeScript generics provided by the AG Grid API (released in version 28)&lt;/li&gt;
&lt;li&gt;How to create a type-safe value getter to retrieve the value for a cell&lt;/li&gt;
&lt;li&gt;How to create a type-safe value formatted to format the value of a cell&lt;/li&gt;
&lt;li&gt;How to create a type-safe and performant cell renderer&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  AG Grid Cell Rendering Pipeline
&lt;/h2&gt;

&lt;p&gt;Without any customization and in the simplest form, each cell in AG Grid is rendered as a string based on the &lt;code&gt;field&lt;/code&gt; specified in the provided row data.&lt;br&gt;
However, often times an AG Grid implementation is not this simple.&lt;br&gt;
This is where we can leverage &lt;a href="https://ag-grid.com/angular-data-grid/cell-content/" rel="noopener noreferrer"&gt;the pipeline for rendering cells&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;valueGetter()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;valueFormatter()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cellRenderer()&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Demo or it didn't happen
&lt;/h2&gt;

&lt;p&gt;Here is a demo using React:&lt;/p&gt;



&lt;p&gt;And, here is a demo using Angular:&lt;/p&gt;


&lt;h2&gt;
  
  
  Using the &lt;code&gt;valueGetter()&lt;/code&gt; callback function
&lt;/h2&gt;

&lt;p&gt;First, we can use a &lt;code&gt;valueGetter()&lt;/code&gt; to fetch and/or mutate data in a cell using a provided callback function.&lt;br&gt;
Let’s take a quick look at an example.&lt;/p&gt;

&lt;p&gt;In this example, the requirement is to create a value getter that is type-safe and uses the data provided to AG Grid to conditionally multiply a value within our data set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;multiplierValueGetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="na"&gt;extends&lt;/span&gt; &lt;span class="na"&gt;Record&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="na"&gt;TKey&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;,
    TKey extends string | number | symbol = string&amp;gt;(
    value: keyof T,
    multiplier: keyof T
  ) =&amp;gt;
    (params: ValueGetterParams&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;): number =&amp;gt; &lt;span class="si"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;multiplier&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="si"&gt;}&lt;/span&gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we declare the &lt;code&gt;multiplierValueGetter()&lt;/code&gt; higher-order function. Using a higher-order function enables us to define the generic type &lt;code&gt;T&lt;/code&gt; that extends a &lt;code&gt;Record&lt;/code&gt; whose values are of type &lt;code&gt;number&lt;/code&gt;. The higher-order function will return the value getter function that will be invoked by AG Grid with provided &lt;code&gt;ValueGetterParams&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;multiplierValueGetter()&lt;/code&gt; has two required parameters, first, the &lt;code&gt;value&lt;/code&gt; property, and second, the &lt;code&gt;multiplier&lt;/code&gt; property, both of which are keys of the data provided to the grid that is of type &lt;code&gt;T&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Because we are using AG Grid v28 (or greater) we can specify the generic type of &lt;code&gt;T&lt;/code&gt; for the &lt;code&gt;ValueGetterParams&lt;/code&gt;. Prior to version 28, this generic type was not available, and as a result the type definition for the &lt;code&gt;data&lt;/code&gt; property was &lt;code&gt;any&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Within the value getter function, if &lt;code&gt;data&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt;, which can be the case when using infinite row model or row grouping in AG Grid, we return &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, we can round the value after multiplying.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example implementation of our &lt;code&gt;multiplierValueGetter()&lt;/code&gt; higher-order function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RowData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Grid&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiplied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Multiplied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;valueGetter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;multiplierValueGetter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiplier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgGridReact&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ag-theme-material"&lt;/span&gt;
      &lt;span class="na"&gt;columnDefs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colDefs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the &lt;code&gt;valueFormatter()&lt;/code&gt; callback function
&lt;/h2&gt;

&lt;p&gt;After the cell value is known, the optional &lt;code&gt;valueFormatter()&lt;/code&gt; callback function enables us to format the value.&lt;br&gt;
Let’s look at an example of using the &lt;code&gt;valueFormatter()&lt;/code&gt; callback function.&lt;/p&gt;

&lt;p&gt;In this example, the requirement is to declare a reusable &lt;code&gt;decimalValueFormatter()&lt;/code&gt; higher-order function that is type-safe and formats the specified data property to a specified length.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decimalValueFormatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValueFormatterParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;minimumFractionDigits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maximumFractionDigits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have declared a &lt;code&gt;decimalValueFormatter()&lt;/code&gt; higher-order function. This enables the implementation of this value formatter to specify two generic types: &lt;code&gt;TData&lt;/code&gt; and &lt;code&gt;TValue&lt;/code&gt;. The generic of &lt;code&gt;TData&lt;/code&gt; represents the type for the &lt;code&gt;data&lt;/code&gt; parameter, and the generic of &lt;code&gt;TValue&lt;/code&gt; represents the type for the &lt;code&gt;value&lt;/code&gt; parameter. Our higher-order function has an optional &lt;code&gt;digits&lt;/code&gt; parameter that specifies the min and maximum number of digits for the decimal formatting. The higher-order function returns a function that is the value getter that is invoked by AG Grid with the &lt;code&gt;ValueGetterParams&amp;lt;TData, TValue&amp;gt;&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;In this value formatter, we are using the &lt;code&gt;Intl.NumberFormat&lt;/code&gt; class to create a new formatter instance, specifying the minimum and maximum number of fraction digits.&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;data&lt;/code&gt; is undefined, which can be the case when using infinite row model or row grouping in AG Grid, then we simply return 0.&lt;/li&gt;
&lt;li&gt;Otherwise, we return the formatted value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example implementation of our &lt;code&gt;decimalValueFormatter()&lt;/code&gt; higher-order function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RowData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;multiplier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardGrid&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiplied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Multiplied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;valueGetter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;multiplierValueGetter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiplier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;valueFormatter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;decimalValueFormatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;taxRate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgGridReact&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ag-theme-material"&lt;/span&gt;
      &lt;span class="na"&gt;colDefs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colDefs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the &lt;code&gt;cellRenderer()&lt;/code&gt; callback function
&lt;/h2&gt;

&lt;p&gt;After the value for a cell is determined, and we have optionally formatted the value, we can use a cell renderer to have full control of how a cell is rendered in AG Grid.&lt;br&gt;
By default, all values are rendered as a string.&lt;br&gt;
In order to render a cell other than a string, we can use a custom cell renderer.&lt;/p&gt;

&lt;p&gt;It’s important to note that we should only use a cell renderer when necessary.&lt;br&gt;
By default, the &lt;code&gt;textContent&lt;/code&gt; of the cell HTML element is set to the (optionally formatted) value.&lt;br&gt;
When we are using a cell renderer we are adding additional elements, event listeners, etc. to the DOM, all of which must be rendered for each cell in the grid.&lt;/p&gt;

&lt;p&gt;Finally, we recommend that all cell renderers &lt;em&gt;strictly use&lt;/em&gt; vanilla JS.&lt;br&gt;
This will improve the paint performance of your application when scrolling the grid.&lt;br&gt;
Why is that?&lt;br&gt;
If you use a framework (e.g. React, Angular, or Vue) then as a result each time the cell needs to be rendered, AG Grid must switch the context to a React (or Angular or Vue) application context in order to render the resulting HTML to the DOM. This can be &lt;em&gt;very&lt;/em&gt; expensive and is often not necessary.&lt;/p&gt;


📣 Only use a cell renderer when necessary, limit the elements and event listeners to a minimum, and always use vanilla JS.


&lt;p&gt;To configure a cell renderer we can provide AG Grid with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A string that references a registered framework component&lt;/li&gt;
&lt;li&gt;A class that implements the &lt;code&gt;ICellRendererComp&lt;/code&gt; interface&lt;/li&gt;
&lt;li&gt;A function that is invoked with the &lt;code&gt;ICellRendererParams&lt;/code&gt; object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at an example. In this example, the user requirement is to display a column with a name that is optionally abbreviated, and, when a user clicks on the name, we want to open a dialog (which will not be the responsibility of AG Grid, but we need to notify the consumer that the user has clicked on the name).&lt;/p&gt;

&lt;p&gt;First, let’s define a new interface that describes the contract between the implementation and the cell renderer for the data that is expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s define another interface for the click event that will notify the implementation that the user has clicked on the name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererClickEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;NameCellRendererClickEvent&lt;/code&gt; describes the event handler object that will be provided to a &lt;code&gt;click&lt;/code&gt; parameter that is implemented when using the cell renderer.&lt;br&gt;
The interface has two generics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, we define a generic of &lt;code&gt;T&lt;/code&gt; that will be provided for the row data.&lt;/li&gt;
&lt;li&gt;Second, we have a generic of &lt;code&gt;E&lt;/code&gt; that has a default assignment to the global &lt;code&gt;Event&lt;/code&gt; interface. In the cell renderer we can set a type that is narrower.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let’s define another interface for the parameters that will be provided to the cell renderer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererClickEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValueGetterParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we have declared the generic type of &lt;code&gt;T&lt;/code&gt; in order to maintain type checking of the &lt;code&gt;params&lt;/code&gt; object that is invoked for the &lt;code&gt;isAbbreviated&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;click&lt;/code&gt; parameter will be a callback function that is invoked by the cell renderer. The callback function is invoked with an &lt;code&gt;event&lt;/code&gt; parameter that is the &lt;code&gt;NameCellRendererClickEvent&lt;/code&gt; interface.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;isAbbreviated&lt;/code&gt; parameter is another callback function that enables the implementing grid to determine if a specific cell value should be abbreviated. We'll use the &lt;code&gt;ValueGetterParams&lt;/code&gt; interface provided by AG Grid to keep our API ergonomic (in that we expect the developer to be aware of this existing interface, so it makes sense to use it).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having described the API, let’s look at the code for the cell renderer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Params&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * AG Grid cell renderer for a user name.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NameCellRenderer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererComp&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** AG Grid API. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GridApi&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** The button element. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** Provided callback function that is invoked when the button is clicked. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererClickEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** The column definition. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;colDef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** The AG Grid column. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Column&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** AG Grid Column API. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;columnApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** AG Grid context. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** The provided data. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** The global document. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** Execution context bound function when the button is clicked. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NameCellRenderer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** Callback function to determinate if the name is abbreviated. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValueGetterParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** AG Grid row node. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** The user name. */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** Value getter params to be provided. */&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;valueGetterParams&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ValueGetterParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;colDef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colDef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;columnApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Params&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setGui&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getGui&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Params&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAbbreviated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueGetterParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;setGui&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-name-cell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAbbreviated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueGetterParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;btnEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;updateParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Params&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colDef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colDef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAbbreviated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^Model/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, phew. Let’s review the code above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we define a new &lt;code&gt;Params&lt;/code&gt; type that is a union of our &lt;code&gt;NameCellRendererParams&lt;/code&gt; interface and the AG Grid provided &lt;code&gt;ICellRendererParams&lt;/code&gt;. The generic type &lt;code&gt;T&lt;/code&gt; is the provided type for the AG Grid row data, which we further provide to the &lt;code&gt;ICellRendererParams&lt;/code&gt; interface. The second typescript generic is explicitly set to &lt;code&gt;string&lt;/code&gt; as we expect that the &lt;code&gt;value&lt;/code&gt; of the cell will always be a string.&lt;/li&gt;
&lt;li&gt;We export the &lt;code&gt;NameCellRenderer&lt;/code&gt; class whose generic type &lt;code&gt;T&lt;/code&gt; extends our previously defined &lt;code&gt;NameCellRendererData&lt;/code&gt; interface. This ensures that we have type safety between the row data provided to AG Grid and our cell renderer. As required, our class implements the &lt;code&gt;ICellRendererComp&lt;/code&gt; interface from AG Grid.&lt;/li&gt;
&lt;li&gt;We have a lot of properties that declared that will have references and values as necessary to pass to the &lt;code&gt;isAbbreviated&lt;/code&gt; provided callback function.&lt;/li&gt;
&lt;li&gt;Note that the &lt;code&gt;click&lt;/code&gt; property is the provided callback function from the implementation that is invoked when the user clicks on the name.&lt;/li&gt;
&lt;li&gt;Further, note that the &lt;code&gt;handleClick&lt;/code&gt; property is an execution-bound function that we’ll use within the cell renderer class for adding and removing the event listener.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;valueGetterParams&lt;/code&gt; property accessor method returns a &lt;code&gt;ValueGetterParams&amp;lt;T&amp;gt;&lt;/code&gt; object that is used by the implementation to determine if a name is abbreviated or not. We have decided to use this interface from AG Grid to keep a consistent API for our users (those developers using our cell renderer in their AG Grid implementations). This is important for API ergonomics.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;init()&lt;/code&gt;, &lt;code&gt;getGui()&lt;/code&gt;, &lt;code&gt;refresh()&lt;/code&gt;, and &lt;code&gt;destroy()&lt;/code&gt; methods are all implemented according to the &lt;code&gt;ICellRendererComp&lt;/code&gt; interface from AG Grid. These methods provide hooks to initialize the cell renderer, provide an HTML element to be appended to the DOM by AG Grid when rendering a cell, and more hooks for when the data is refreshed and when the cell is destroyed. It’s important that we use the &lt;code&gt;destroy()&lt;/code&gt; lifecycle method to do any necessary cleanup, such as removing event listeners, to prevent memory leaks in our application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, here is an example implementation of the &lt;code&gt;NameCellRenderer&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RowData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardGrid&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;cellRenderer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NameCellRenderer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cellRendererParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`You clicked: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isAbbreviated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;NameCellRendererParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgGridReact&lt;/span&gt;
      &lt;span class="na"&gt;colDefs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colDefs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;So In summary, we have learned how AG Grid renders a cell, and how we can provide data to a cell, optionally format a cell, and if necessary, customize the rendering of a cell.&lt;br&gt;
The key takeaways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;valueGetter()&lt;/code&gt; callback function to fetch and/or mutate the value of a cell.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;valueFormatter()&lt;/code&gt; callback function to format a cell’s value.&lt;/li&gt;
&lt;li&gt;If necessary, provide a cell renderer to customize the HTML of a cell.&lt;/li&gt;
&lt;li&gt;Cell renderers can also be interactive, invoke callback functions, and more.&lt;/li&gt;
&lt;li&gt;It’s important to remove event listeners when a cell is destroyed.&lt;/li&gt;
&lt;li&gt;Design an API that is ergonomic.&lt;/li&gt;
&lt;li&gt;Create value getters, value formatters, and cell renderers that are type safe.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>react</category>
      <category>angular</category>
    </item>
    <item>
      <title>20 Questions on Product Validation</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Wed, 22 Jun 2022 17:04:26 +0000</pubDate>
      <link>https://forem.com/blove/20-questions-on-product-validation-j11</link>
      <guid>https://forem.com/blove/20-questions-on-product-validation-j11</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"The biggest men and women with the biggest ideas can be shot down by the smallest men and women with the smallest minds.&lt;/p&gt;

&lt;p&gt;Think big anyway.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first step is thinking big. The second step is validation. So, how do we validate a digital product?&lt;/p&gt;

&lt;p&gt;Let’s answer this question with more questions - specifically, 20 more questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is product validation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Product validation is a systematic approach to measuring a concept with potential customers. This is the moment of truth - if you build it, will they come?&lt;/p&gt;

&lt;p&gt;Product validation does not ensure the success of a business or product. Far from it. It only enables you to have insight into the validity of a concept. It’s very likely that your concept is not valid, or, perhaps that your concept needs to pivot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is product validation necessary?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Product validation is necessary to understand your target customer. Without product validation, a startup will waste time - and money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When should I validate?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As early as you can. A drawing on a napkin will do - starting validation of a product does not require sophisticated wireframes, user requirements, high-fidelity mockups, or an impressive presentations. Presenting concepts that invoke meaningful feedback is the key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How often should I validate?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Product validation should be an ongoing part of your design process that occurs after each iteration of a prototype.&lt;/p&gt;

&lt;p&gt;At LiveLoveApp, we use Google’s approach to design sprints that is a sequence of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understand&lt;/li&gt;
&lt;li&gt;Define&lt;/li&gt;
&lt;li&gt;Sketch&lt;/li&gt;
&lt;li&gt;Decide&lt;/li&gt;
&lt;li&gt;Prototype&lt;/li&gt;
&lt;li&gt;Validate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There it is - after each iteration of a prototype when building a product we always want to validate the concept with real users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I find early adopters?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not everyone is your target customer. I mean, it’s likely not your mum - she’s likely going validate every concept. That’s not very useful.&lt;/p&gt;

&lt;p&gt;Early adopters are interested in providing influence on the development of an early technology in return for prestige, gaining a competitive advantage, or having lifetime access to a product. Early adopters may be within the reach of your network, but they are likely one or two degrees removed. Strategies such as pre-release, invite-only access, and possibly, lifetime access can lure early adopters to your product. An often overlooked but valuable strategy for getting the attention of your early adopters is through direct communication channels for your industry and your target customers such as online groups, forums, in-person meetups, and social media.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What feedback is useful?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Useful feedback should measure both the satisfaction of the user and the result of a user completing a specific task using your product.&lt;/p&gt;

&lt;p&gt;Gathering holistic feedback that is vague and filled with general platitudes is not useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if they’re wrong?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All user feedback should be noted, but not all user feedback is valuable. If &lt;em&gt;all&lt;/em&gt; of your user feedback is perceived as wrong, well then, you’re probably wrong. It’s important to dig deeper if you believe validation feedback is incorrect. This may be an opportunity to identify a pivot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I gather feedback?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gather feedback through a usability study and a cognitive walkthrough. A usability study focuses on the user interface of your product to determine if the prototype’s user interface is acceptable or needs to be improved. A cognitive walkthrough evaluates the product’s ability to solve a specific task. It’s important to instruct users that you are not testing them, rather, you are testing the product. Finally, coach your users to think aloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I use unmoderated tools?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Moderated usability studies are those in which a person that is familiar with the product facilitates. Moderated usability studies and cognitive walk-throughs provide the most effective feedback for validation. On the other hand, Unmoderated usability studies do not have a person present for facilitation. Similarly, unmoderated tools can measure and monitor product use, providing insight into how a user discovers and interacts with your product without guidance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a prototype?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. However, your prototype does not necessarily need to be high fidelity. It is useful to gather feedback through low-fi prototypes before iterating toward high-fi prototypes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need an MVP?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. A minimum viable product (MVP) should only be developed after prototype iteration. Only upon completion - and validation - of a prototype should an MVP be developed.&lt;/p&gt;

&lt;p&gt;An MVP is yet another opportunity to validate and gather feedback. The breakeven of the MVP development expense should ideally equal the value of the feedback and validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to generate revenue?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The goal of the product is to generate revenue (though, not necessarily as may be the case for a public works product, government product, not-for-profit products, etc). However, the goal of product validation is not to generate revenue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I presell?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using a pre-selling strategy for product validation can provide both feedback and revenue. Preselling can also build community and urgency. The risk is that things can go wrong, so it’s important to clearly communicate a refund policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about LTDs?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lifetime deal (LTD) provides an opportunity to attract early adopters with the ongoing expense of providing access to your product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I reveal my product roadmap?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Revealing a product roadmap to early adopters provides validation of feature expansions. However, this could also provide existing competitors and would-be competitors with insight into your future features. It is important to consider the size of the moat around your product and to use legal non-disclosure agreements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I have a public beta?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A public beta provides further validation of your product through metrics and can build community and urgency. A public beta can be used effectively in conjunction with preselling. The risks are negative reviews or early adopters vomiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I raise capital from early adopters?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Raising capital from early adopters enables you to retain equity before potential liquidation during capital raising events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I identify potential pivots?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Product validation may reveal opportunities for a pivot of your product. When gathering feedback it is important to note problems that a user identifies with the user interface and how they use the product, but also problems that exist that the product does not solve. Those problems may be beyond the scope of your existing product. As with all products, validating a potential pivot is critical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the risks with product validation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The goal of product validation is to reduce risk, however, there are some risks associated with product validation. Preselling can introduce risk, flopped beta launches can be risky, and leaks to existing and would-be competitors could risk your (hopeful) blue ocean. Mitigate these risks through clear communication, striving for excellence, and legal containment.&lt;/p&gt;

</description>
      <category>product</category>
      <category>prototyping</category>
      <category>startup</category>
      <category>design</category>
    </item>
    <item>
      <title>Angular 14 Standalone Components</title>
      <dc:creator>Brian Love</dc:creator>
      <pubDate>Fri, 17 Jun 2022 17:50:59 +0000</pubDate>
      <link>https://forem.com/blove/angular-14-standalone-components-5ghj</link>
      <guid>https://forem.com/blove/angular-14-standalone-components-5ghj</guid>
      <description>&lt;p&gt;Angular version 14 is a feature-packed release that brings new APIs, functionality, and a developer experience. Angular version 14 is arguably the biggest release since version 9 when Angular’s newest compilation and rendering engine (called Ivy) was released.&lt;/p&gt;

&lt;p&gt;This article is the first in a series that will cover the key features and takeaways that I believe angular developers and engineering managers should know about Angular version 14. First, we’ll start with the hottest new topic called standalone components. Then, we’ll dive into typed forms, what they are, and what this means for you and your organization. We’ll talk about version dependency updates, improvements to the Angular Language Service, new configurations to improve testing at runtime, and a small compiler update.&lt;/p&gt;

&lt;p&gt;Let’s dive into Angular’s new developer preview of standalone components!&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular 14 Playground on Stackblitz
&lt;/h2&gt;

&lt;p&gt;Real quick - before we dive into the details - I want to mention that I have an Angular 14 Playground for you on Stackblitz. Much of the example code below is referenced from this small project. Check it out and feel free to fork it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-v14-playground?file=src%2Fmain.ts,src%2Fabout.component.ts" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/angular-v14-playground?file=src%2Fmain.ts,src%2Fabout.component.ts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is a standalone component?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Prior to version 14, all components had to be declared as part of the &lt;code&gt;declarations&lt;/code&gt; array in an &lt;code&gt;NgModule&lt;/code&gt;. NgModules are a critical building piece to solve architectural challenges in Angular, namely importing other modules in your codebase or importing other modules that are published as part of a library (using the Angular Package Format. NgModules also provide a mechanism for wiring up and configuring dependency injection. We’ll discuss both of these in more detail below.&lt;/p&gt;

&lt;p&gt;Standalone components enable Angular developers to build applications &lt;em&gt;without&lt;/em&gt; using the &lt;code&gt;NgModule&lt;/code&gt; based approach.&lt;/p&gt;

&lt;p&gt;💡 In this article, I will often refer to Angular’s &lt;code&gt;NgModule&lt;/code&gt; simply as a “module”. This is in an effort to improve readability and is not meant to be confused with ECMAScript modules.&lt;/p&gt;

&lt;p&gt;The immediate question is &lt;strong&gt;what about applications built today with modules?&lt;/strong&gt; Will modules be supported in the future?&lt;/p&gt;

&lt;p&gt;The answer is a resounding yes. Angular applications, and libraries, that are built with modules will continue to be supported. Flatly stated, modules are not going anywhere.&lt;/p&gt;

&lt;p&gt;Further, Angular’s new standalone component architecture is fully compatible with the existing module-based architecture. You can continue to use modules where necessary and/or preferred, and you can start using standalone components alongside them. Based on your team and organization’s architectural style, you can start adopting standalone components, or you can continue to build Angular applications using modules as you have been doing for the past 6 years. This continues on the Angular Team’s promise to not leave anyone behind with breaking changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will standalone components replace modules as the de facto style?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the time of this writing, as Angular version 14 is just now being released, the answer to this question is unknown. This will likely depend on community adoption and tooling. Further, the current documentation, getting started journey, and style guide do not teach standalone components over the module-based approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why are standalone components in “developer preview”?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Standalone components are being released in version 14 as a developer preview. This means that the LTS policy for the APIs does &lt;em&gt;not&lt;/em&gt; apply to standalone components. This is a good thing! As the community adopts this new architectural pattern we will all learn from each other what works well, what is cumbersome, and, potentially, what are the edge cases where this architectural pattern breaks. This learning enables the framework to innovate at a fast pace. It also means that the APIs, while public, &lt;em&gt;may&lt;/em&gt; change in future minor releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with standalone Components
&lt;/h2&gt;

&lt;p&gt;To use standalone components, Angular has introduced a new &lt;code&gt;standalone&lt;/code&gt; property in the component metadata. The property is &lt;code&gt;false&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;Here is a quick example of a standalone component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;, {{ name }}.
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;``&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NameComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code example above is like any Angular component except that we have set the &lt;code&gt;standalone&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt;. This instructs the compiler to treat this component as standalone, and further, this prevents us from including the component in the &lt;code&gt;declarations&lt;/code&gt; array of a module.&lt;/p&gt;

&lt;p&gt;Standalone components must declare their own dependencies including child standalone components. For example, to use the &lt;code&gt;&amp;lt;app-name&amp;gt;&lt;/code&gt; component in another standalone component, I must import the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;app-name [name]="name"&amp;gt;Hi&amp;lt;/app-name&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NameComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Brian Love&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code example above note that I have included our &lt;code&gt;NameComponent&lt;/code&gt; in the &lt;code&gt;imports&lt;/code&gt; array in the component metadata. This instructs the compiler that this component is a dependency of the &lt;code&gt;AppComponent&lt;/code&gt;, which is also a standalone component.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about existing &lt;code&gt;NgModule&lt;/code&gt; uses?
&lt;/h2&gt;

&lt;p&gt;As stated previously, standalone components are fully compatible with existing codebases that use the &lt;code&gt;NgModule&lt;/code&gt; pattern. If a standalone component uses a directive, component, or pipe that is exported from a module then we include the module in the &lt;code&gt;imports&lt;/code&gt; array in the standalone component metadata to import the module. All publicly exported members of the module are now available for use in the standalone component.&lt;/p&gt;

&lt;p&gt;Let’s expand our current example application to use Angular Material. To do so, we’ll need to import the necessary modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatIconModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatListModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatSidenavModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatToolbarModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;mat-toolbar class="toolbar"&amp;gt;
      &amp;lt;button mat-icon-button aria-label="Toggle menu" (click)="toggleMatSidenav()"&amp;gt;
        &amp;lt;mat-icon&amp;gt;menu&amp;lt;/mat-icon&amp;gt;
      &amp;lt;/button&amp;gt;
      &amp;lt;span&amp;gt;Angular v14 Playground&amp;lt;/span&amp;gt;
    &amp;lt;/mat-toolbar&amp;gt;
    &amp;lt;mat-sidenav-container class="container"&amp;gt;
      &amp;lt;mat-sidenav [(opened)]="matSidenavOpened" fixedTopGap="64" fixedInViewport&amp;gt;
        &amp;lt;mat-nav-list&amp;gt;
          &amp;lt;a mat-list-item routerLink="/"&amp;gt;Home&amp;lt;/a&amp;gt;
          &amp;lt;a mat-list-item routerLink="/about"&amp;gt;About&amp;lt;/a&amp;gt;
          &amp;lt;a mat-list-item href="https://liveloveapp.com" target="_blank"&amp;gt;Learn More about LiveLoveApp&amp;lt;/a&amp;gt;
        &amp;lt;/mat-nav-list&amp;gt;
      &amp;lt;/mat-sidenav&amp;gt;
      &amp;lt;mat-sidenav-content class="content"&amp;gt;
        &amp;lt;main&amp;gt;
          &amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
        &amp;lt;/main&amp;gt;
      &amp;lt;/mat-sidenav-content&amp;gt;
    &amp;lt;/mat-sidenav-container&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;matSidenavOpened&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Brian Love&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;toggleMatSidenav&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matSidenavOpened&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matSidenavOpened&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s review the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, you’ll note that the &lt;code&gt;AppComponent&lt;/code&gt; has the &lt;code&gt;standalone&lt;/code&gt; property set to &lt;code&gt;true&lt;/code&gt; in the component metadata.&lt;/li&gt;
&lt;li&gt;I went ahead and update the change detection strategy, really just to test out how this works with standalone components, and thankfully, it works as expected.&lt;/li&gt;
&lt;li&gt;Note the &lt;code&gt;imports&lt;/code&gt; array. I’ve imported the necessary material modules that I need for the component. I’ve also imported the &lt;code&gt;RouterModule&lt;/code&gt; since my component’s template includes the &lt;code&gt;&amp;lt;router-outlet&amp;gt;&lt;/code&gt; custom element.&lt;/li&gt;
&lt;li&gt;For the sake of brevity, I skipped the styles (but you can check out the full &lt;a href="https://stackblitz.com/edit/angular-v14-playground?file=src%2Fmain.ts" rel="noopener noreferrer"&gt;Angular v14 Playground demo on Stackblitz&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dependency Injection with standalone Components
&lt;/h2&gt;

&lt;p&gt;Before we dive into a few of the important updates in Angular v14 to support standalone components, let me reiterate a few things.&lt;/p&gt;

&lt;p&gt;First, the developer experience for module-based Angular applications using dependency injection has no breaking changes, and for the most part, has not changed. You can continue to use the injector, injection tokens, providers, and the &lt;code&gt;@Injectable()&lt;/code&gt; decorator just as you have prior to Angular version 14.&lt;/p&gt;

&lt;p&gt;Second, the dependency injector hierarchy is still &lt;em&gt;very&lt;/em&gt; similar, with a few exceptions that we’ll cover shortly.&lt;/p&gt;

&lt;p&gt;Module type injectors are available using the &lt;code&gt;providers&lt;/code&gt; array within the &lt;code&gt;NgModule&lt;/code&gt; metadata as well as by using the &lt;code&gt;providedIn&lt;/code&gt; decorator. Specifying the &lt;code&gt;root&lt;/code&gt; value for the &lt;code&gt;providedIn&lt;/code&gt; decorator will register the class at the root level that is available throughout your Angular application.&lt;/p&gt;

&lt;p&gt;Here is a quick example of module type injectors that you are likely familiar with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Module type injector using NgModule metadata&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MAT_FORM_FIELD_DEFAULT_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// Module type injector using providedIn property&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node type injectors enable us to limit the provider scope through the use of the &lt;code&gt;providers&lt;/code&gt; array for a directive or component. One common use case for limit provider scope is when you are using NgRx’s Component Store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// node type injector&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;GridComponentStore&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GridComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s learn some of the new terminology and features introduced in Angular version 14.&lt;/p&gt;

&lt;p&gt;First, Angular 14 adds a new &lt;code&gt;viewProviders&lt;/code&gt; property to the &lt;code&gt;@Component()&lt;/code&gt; metadata that enables us to further limit the provider scope to children of the existing component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// node type injector usin `viewProviders`&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    Hello, {{ user$ | async | greet }}
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NameComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;user$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;viewProviders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;app-name&amp;gt;&amp;lt;/app-name&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToolbarComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, Angular 14 introduces a new term called “Environment Injectors”. Environment injectors cover the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Module type injectors. As discussed above, this includes providers declared in a module as well as those that use the &lt;code&gt;providedIn&lt;/code&gt; property for the &lt;code&gt;@Injectable()&lt;/code&gt; metadata.&lt;/li&gt;
&lt;li&gt;Providers that are declared when the application is bootstrapped.&lt;/li&gt;
&lt;li&gt;Providers that are declared within the &lt;code&gt;providers&lt;/code&gt; array for a &lt;code&gt;Route&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at an example of declaring providers when an application is bootstrapped. Common use cases include providing the &lt;code&gt;BrowserAnimationsModule&lt;/code&gt;, registering root-level routes using the &lt;code&gt;RouterModule.forRoot()&lt;/code&gt; static method, and registering NgRx’s global store using the &lt;code&gt;StoreModule.forRoot()&lt;/code&gt; static method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;importProvidersFrom&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;BrowserAnimationsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above we are also introducing the &lt;code&gt;importProvidersFrom()&lt;/code&gt; function. This utility function collects all providers from one or more sources that are either a standalone component or an &lt;code&gt;NgModule&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also introduced in Angular 14, we can declare an array of providers within a &lt;code&gt;Route&lt;/code&gt; configuration that will create an Environment Injector at the route level. This enables the providers to be used within all components within the route, and all child routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./users.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;UsersModule&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;UsersModule&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MAT_FORM_FIELD_DEFAULT_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;redirectTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, Angular 14 introduces an additional injector type that is termed a “Standalone Injector”. No, the poor injector is not standing alone on the playground without any friends. The Standalone Injector is a child of the root environment injector and is responsible for isolating all providers for standalone components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing with standalone Components
&lt;/h2&gt;

&lt;p&gt;Angular 14 introduces an expanded API for routing with standalone components, including a feature that enables us to &lt;em&gt;very&lt;/em&gt; easily lazy load a standalone component. If you are familiar with the router’s &lt;code&gt;loadChildren&lt;/code&gt; configuration property that enables lazy-loading modules, then you will be very comfortable using the new &lt;code&gt;loadComponent&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./about.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;AboutComponent&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;AboutComponent&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code example above we are lazy loading a single standalone component at runtime with a simple configuration within the route.&lt;/p&gt;

&lt;p&gt;Angular 14 also introduces a new feature that enables us to lazy load routes without the need to wrap them up in an &lt;code&gt;NgModule&lt;/code&gt; using the &lt;code&gt;RouterModule.forChild()&lt;/code&gt; static method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./admin/routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, that in order to use this new feature, all routes must use standalone components. This feature is not compatible with existing non-standalone components defined within the routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, Angular version 14 shipped a new developer preview of the standalone components API. This API enables Angular developers to build applications without the use of the &lt;code&gt;NgModule&lt;/code&gt; architecture. The primary goals of standalone components are to simplify the API, improve developer ergonomics and velocity, and to enable future innovation in the Angular ecosystem. Standalone components do introduce some changes to the dependency injection system and the routing story. Finally, we should note that this new feature is backward compatible with existing Angular code that uses the &lt;code&gt;NgModule&lt;/code&gt; architecture, and that this is a developer preview - meaning that the API is not finalized and could have breaking changes in the future.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
