{"id":35359,"date":"2026-11-12T01:22:27","date_gmt":"2026-11-12T00:22:27","guid":{"rendered":"https:\/\/www.angulararchitects.io\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/"},"modified":"2026-11-12T01:52:11","modified_gmt":"2026-11-12T00:52:11","slug":"fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/","title":{"rendered":"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models"},"content":{"rendered":"<div class=\"wp-post-series-box series-agentic-angular wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-agentic-angular6af71ec3707f8\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-agentic-angular6af71ec3707f8\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 12 of 12 in the series <em>&ldquo;Agentic UI with Angular&rdquo;<\/em>\t\t<\/p>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/understanding-ag-ui-the-standard-for-agentic-user-interfaces\/\">Understanding AG-UI: The Standard for Agentic User Interfaces<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/ag-ui-in-practice-the-sdk-for-typescript\/\">AG-UI in Practice: The SDK for TypeScript<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/ag-ui-in-practice-ag-ui-end-to-end-connecting-server-and-client\/\">AG-UI End to End: Connecting Server and Client<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\">Agentic UI with Angular, CopilotKit, and AG-UI<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-how-ai-generates-dynamic-uis-at-runtime\/\">A2UI in Angular: How AI Generates Dynamic UIs at Runtime<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/integrating-a2ui-with-ag-ui-in-angular\/\">Integrating A2UI with AG-UI and CopilotKit in Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\">Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-i-made-my-a2ui-dashboard-300-times-faster\/\">A2UI with a DSL: Controllable Dashboards Optimized for Performance<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/agentic-ui-with-mcp-apps-tool-results-as-interactive-widgets\/\">Agentic UI with MCP Apps: Tool Results as Interactive Widgets<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/mcp-apps-in-angular-with-copilotkit-rich-chat-interfaces-instead-of-text-responses\/\">MCP Apps in Angular with CopilotKit: Rich Chat Interfaces Instead of Text Responses<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-express-token-optimized-generative-uis\/\">A2UI Express: Token-Optimized Generative UIs for Local and Cloud Models<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models<\/span><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p><em>Coarse-grained building blocks instead of single widgets: how A2UI macros make Generative UIs faster and cheaper with cloud models and pave the way for local ones<\/em><\/p>\n<p>A2UI describes user interfaces with simple building blocks such as <code>Text<\/code>, <code>Row<\/code>, <code>Column<\/code>, and <code>Button<\/code>. This fine granularity makes the protocol flexible. But it comes at a price: a dashboard with six tiles quickly consists of dozens of components. The model has to generate each of them token by token, which costs time and money. This applies to cloud models and even more to local models that run on your own hardware.<\/p>\n<p>The solution is coarse-grained building blocks. Instead of describing every widget, the model only names the tiles it needs and their most important properties. The server then resolves these building blocks into regular A2UI. <a href=\"https:\/\/a2ui.org\/guides\/macros\/\">A2UI macros<\/a> build on this idea.<\/p>\n<p>In this article, I show how A2UI macros work, how to use them with A2UI Express, and how a simple, generic macro mechanism brings them to TypeScript.<\/p>\n<p>\ud83d\udcc2 <a href=\"https:\/\/github.com\/angular-architects\/flights42\/tree\/AGU-a2ui-express\">Source code<\/a> (branch <code>AGU-a2ui-express<\/code>)<\/p>\n<p>\ud83d\udcc2 <a href=\"https:\/\/github.com\/angular-architects\/flights42\/tree\/AGU-gemma\">Source code updated for Gemma 4<\/a> (branch <code>AGU-gemma<\/code>)<\/p>\n<h2>A Dashboard Made of Tiles<\/h2>\n<p>The example application contains a dashboard that users describe in natural language. An agent assembles the requested tiles from this description:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/dashboard.png\" alt=\"A dashboard built from a natural-language request: a boarding pass, booked flights, a flight search, rental cars, hotels, and the weather at the destinations.\" \/><\/p>\n<p>In the first version, the model generated the entire A2UI itself. A single dashboard took about 40 seconds and consumed more than 46,000 tokens. The main drivers were a long prompt with examples and the extensive structure the model had to write.<\/p>\n<p>A DSL on a higher level of abstraction is much more efficient. It describes each tile with a handful of properties, such as a route or a maximum number of rows. The model only generates this compact description, and code on the server translates it into A2UI. This brought the duration down to a few seconds and cut the token consumption by a factor of 30. An additional cache for the DSL brings recurring dashboards down to about 0.1 seconds:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/a2ui-optimization.png\" alt=\"The model generates the DSL. The server caches it and derives the A2UI structure and data from it.\" \/><\/p>\n<p>My article <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-i-made-my-a2ui-dashboard-300-times-faster\/\">How I Made My A2UI Dashboard 300 Times Faster<\/a> describes the details.<\/p>\n<p>The DSL has also proven itself with local models. With it, moving the fairly complex dashboard to Gemma 4 took little effort. You can run this open-weight model on your own hardware. My article <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/optimizing-agentic-applications-for-local-models-patterns-for-speed-and-reliability\/\">Optimizing Agentic Applications for Local Models<\/a> has more on this.<\/p>\n<h2>A2UI Macros: The DSL Comes to the A2UI SDKs<\/h2>\n<p>You could always define your own DSL, but you had to specify it and translate it into A2UI yourself. The Python SDK for A2UI was the first to support such a DSL at SDK level, which simplifies the implementation. With macros, you define composite components on the server. <\/p>\n<p>This article uses TypeScript, but a short look at the original Python API shows the idea behind macros. There, a macro is a function that returns a tree of base components:<\/p>\n<pre><code class=\"language-python\"># Example from the documentation of the A2UI Python SDK (shortened)\nfrom a2ui.builder.v1_0 import Action, Button, Card, Column, DynamicString, Text\nfrom a2ui.transformers.macros import macro\n\n@macro\ndef ProductCard(title: DynamicString, price: str, on_buy: Action) -&gt; Card:\n    &quot;&quot;&quot;Renders a product showcase card.\n\n    Args:\n        title: Display name of the product.\n        price: Formatted price string.\n        on_buy: Action triggered when the user clicks buy.\n    &quot;&quot;&quot;\n    return Card(\n        child=Column(\n            children=[\n                Text(text=title, variant=&quot;body&quot;),\n                Text(text=price, variant=&quot;caption&quot;),\n                Button(child=Text(text=&quot;Buy Now&quot;), action=on_buy),\n            ]\n        )\n    )<\/code><\/pre>\n<p>A so-called <code>MacroExpander<\/code> then resolves the macros into A2UI. It works recursively, so macros can build on other macros. The <a href=\"https:\/\/a2ui.org\/guides\/macros\/\">documentation on A2UI macros<\/a> has more details.<\/p>\n<h3>A Dashboard in A2UI Express<\/h3>\n<p>The example application defines a macro for each tile. In A2UI Express, the compact inference format of A2UI, the model describes the dashboard from above like this:<\/p>\n<pre><code class=\"language-ts\">&lt;a2ui&gt;\nroot = Dashboard([\n  BoardingPasses(2),\n  BookedFlightsList(showWeather=false),\n  FlightSearch(&quot;Graz&quot;, &quot;Hamburg&quot;),\n  RentalCars(),\n  Hotels(),\n  WeatherList()\n])\n&lt;\/a2ui&gt;<\/code><\/pre>\n<p>The macro calls take required arguments by position and optional arguments by name. <code>Dashboard<\/code> is a macro itself and takes the tiles as children. The <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-express-token-optimized-generative-uis\/\">previous article<\/a> shows how A2UI Express works in detail.<\/p>\n<p>These few lines expand into regular A2UI. The call <code>BookedFlightsList(showWeather=false)<\/code> alone turns into 11 components:<\/p>\n<pre><code class=\"language-json\">&quot;components&quot;: [\n  { &quot;id&quot;: &quot;root&quot;, &quot;component&quot;: &quot;Column&quot;, &quot;children&quot;: [&quot;root_children_0&quot;, &quot;root_children_1&quot;, [...]] },\n  [...]\n  { &quot;id&quot;: &quot;root_children_1&quot;, &quot;component&quot;: &quot;Card&quot;, &quot;child&quot;: &quot;root_children_1-body&quot; },\n  { &quot;id&quot;: &quot;root_children_1-body&quot;, &quot;component&quot;: &quot;Column&quot;, &quot;children&quot;: [&quot;root_children_1-title&quot;, &quot;root_children_1-list&quot;, &quot;root_children_1-empty&quot;] },\n  { &quot;id&quot;: &quot;root_children_1-title&quot;, &quot;component&quot;: &quot;Text&quot;, &quot;text&quot;: &quot;My booked flights&quot;, &quot;variant&quot;: &quot;h2&quot; },\n  {\n    &quot;id&quot;: &quot;root_children_1-list&quot;,\n    &quot;component&quot;: &quot;Column&quot;,\n    &quot;children&quot;: { &quot;componentId&quot;: &quot;root_children_1-row&quot;, &quot;path&quot;: &quot;\/root_children_1\/flights&quot; }\n  },\n  { &quot;id&quot;: &quot;root_children_1-row&quot;, &quot;component&quot;: &quot;Row&quot;, &quot;align&quot;: &quot;start&quot;, &quot;children&quot;: [&quot;root_children_1-row-col&quot;] },\n  { &quot;id&quot;: &quot;root_children_1-row-col&quot;, &quot;component&quot;: &quot;Column&quot;, &quot;weight&quot;: 3, &quot;children&quot;: [&quot;root_children_1-row-route&quot;, &quot;root_children_1-row-meta&quot;, &quot;root_children_1-row-btn&quot;] },\n  { &quot;id&quot;: &quot;root_children_1-row-route&quot;, &quot;component&quot;: &quot;Text&quot;, &quot;text&quot;: { &quot;path&quot;: &quot;route&quot; }, &quot;variant&quot;: &quot;h3&quot; },\n  { &quot;id&quot;: &quot;root_children_1-row-meta&quot;, &quot;component&quot;: &quot;Text&quot;, &quot;text&quot;: { &quot;path&quot;: &quot;meta&quot; }, &quot;variant&quot;: &quot;body&quot; },\n  { &quot;id&quot;: &quot;root_children_1-empty&quot;, &quot;component&quot;: &quot;Text&quot;, &quot;text&quot;: { &quot;path&quot;: &quot;\/root_children_1\/emptyMessage&quot; }, &quot;variant&quot;: &quot;body&quot; },\n  {\n    &quot;id&quot;: &quot;root_children_1-row-btn&quot;,\n    &quot;component&quot;: &quot;Button&quot;,\n    &quot;child&quot;: &quot;root_children_1-row-btn-label&quot;,\n    &quot;action&quot;: {\n      &quot;event&quot;: {\n        &quot;name&quot;: &quot;checkIn&quot;,\n        &quot;context&quot;: { &quot;flightId&quot;: { &quot;path&quot;: &quot;id&quot; } }\n      }\n    }\n  },\n  { &quot;id&quot;: &quot;root_children_1-row-btn-label&quot;, &quot;component&quot;: &quot;Text&quot;, &quot;text&quot;: &quot;Check in&quot; },\n  [...]\n]<\/code><\/pre>\n<p>The column <code>root_children_1-list<\/code> uses a template: the client repeats the row <code>root_children_1-row<\/code> for each entry of the list <code>\/root_children_1\/flights<\/code> in the data model. Inside the template, relative paths such as <code>route<\/code> refer to the current entry.<\/p>\n<p>The entire dashboard consists of 45 components. As compact JSON, this structure takes about 1,300 tokens, the Express code with macros fewer than 50. I counted with OpenAI's tokenizer <code>o200k_base<\/code>. These numbers do not include the data itself, as the components get it via data binding.<\/p>\n<p><div style=\"\nmargin: 8px 0;\npadding: 22px;\nborder: 1px solid #e5e7eb;\nborder-radius: 14px;\nbackground: #f8fafc;\n\"><\/p>\n<h3 style=\"margin-top:0\">Agentic UI with Angular<\/h3>\n<p>If you want to embed Generative UIs cleanly into larger Angular applications rather than just here and there: In my book Agentic UI with Angular, I cover the underlying patterns and trade-offs in depth, including DSLs, caching, and open standards such as AG-UI, A2UI, and MCP.<\/p>\n<p><a href=\"https:\/\/agentic-angular.com\/\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/cover.png\" width=\"400\" alt=\"Cover of the eBook Agentic UI with Angular\" style=\"cursor:pointer !important\"><\/a><\/p>\n<p><a style=\"cursor:pointer !important\" href=\"https:\/\/agentic-angular.com\/\">More about the eBook \u2192<\/a>\n<\/div>\n<\/p>\n<h2>Implementation with TypeScript<\/h2>\n<p>When I wrote this article, the TypeScript SDK <code>@a2ui\/agent<\/code> did not support macros yet. The example application therefore combines the Express parser of the SDK with a simple, generic macro mechanism of its own. It consists of about 170 lines and knows nothing about the dashboard.<\/p>\n<p>In this article, I treat this macro mechanism as a black box and only show how the application uses it. If you are interested in its implementation, you find it <a href=\"https:\/\/github.com\/angular-architects\/flights42\/tree\/AGU-a2ui-express\/ai-server\/src\/mastra\/a2ui\/simple-macros\">in the example repository<\/a>. The following diagram shows how the parser and the macro mechanism work together:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/macro-pipeline-scaled.png\" alt=\"The model writes A2UI Express with macros. The A2UI SDK parses and validates the Express code. The function applyMacros loads the data for each macro and replaces it with base components.\" \/><\/p>\n<h3>Defining Macros<\/h3>\n<p>A macro consists of a Zod schema for its arguments, an optional function <code>load<\/code> for its data, and a function <code>render<\/code> that returns the components replacing the macro. For each tile, a Zod schema describes the arguments:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/dashboard-spec.ts\nimport { z } from &#039;zod&#039;;\n\nimport type { MacroInput } from &#039;..\/a2ui\/simple-macros\/macro.js&#039;;\n\nconst positiveInt = z.number().int().positive();\n\n[...]\n\nexport const bookedFlightsListSchema = z.object({\n  component: z.literal(&#039;BookedFlightsList&#039;),\n  showCheckInButton: z\n    .boolean()\n    .optional()\n    .describe(\n      &#039;Whether to render a &quot;Check in&quot; button per booked flight. Defaults to true.&#039;,\n    ),\n  showWeather: z\n    .boolean()\n    .optional()\n    .describe(\n      &#039;Whether to enrich each flight entry with a weather forecast for the destination. Defaults to true.&#039;,\n    ),\n  maxRows: positiveInt\n    .optional()\n    .describe(\n      &#039;Maximum number of booked flights to list. Defaults to no limit.&#039;,\n    ),\n});\n\nexport type BookedFlightsListNode = MacroInput&lt;typeof bookedFlightsListSchema&gt;;\n\n[...]\n\nexport const dashboardSchema = z.object({\n  component: z.literal(&#039;Dashboard&#039;),\n  children: z\n    .array(z.string())\n    .meta({ $ref: &#039;common_types.json#\/$defs\/ChildList&#039; }),\n});\n\nexport type DashboardNode = MacroInput&lt;typeof dashboardSchema&gt;;<\/code><\/pre>\n<p>As with every A2UI component, the property <code>component<\/code> defines the name. The descriptions end up in the JSON schema of the catalog. For the children, the schema <code>dashboardSchema<\/code> refers to the type <code>ChildList<\/code> from the common types of A2UI. This way, the parser treats the calls in this list as child components.<\/p>\n<p>The type <code>MacroInput<\/code> of the macro mechanism describes a macro call as a node: an object with the arguments and the id that the Express parser assigns to the call. The functions <code>load<\/code> and <code>render<\/code> receive such a node.<\/p>\n<p>The object <code>dashboardMacros<\/code> combines each schema with a loader and a renderer:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/dashboard-macros.ts\nimport { defineMacro } from &#039;..\/a2ui\/simple-macros\/macro.js&#039;;\n[...]\n\nexport const dashboardMacros = {\n  Dashboard: defineMacro({\n    schema: dashboardSchema,\n    render: (node) =&gt; {\n      return buildDashboard(node);\n    },\n  }),\n  [...]\n  BookedFlightsList: defineMacro({\n    schema: bookedFlightsListSchema,\n    load: (node) =&gt; {\n      return loadBookedFlightsList(node);\n    },\n    render: (node) =&gt; {\n      return buildBookedFlightsList(node);\n    },\n  }),\n  [...]\n};<\/code><\/pre>\n<p>The function <code>defineMacro<\/code> returns the macro unchanged. It only exists so that TypeScript derives the type of the node from the schema.<\/p>\n<h3>Loading and Rendering<\/h3>\n<p>The root component <code>Dashboard<\/code> is a macro, too. It needs no data, and its renderer turns it into a <code>Column<\/code> with the same children:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/dashboard-renderers.ts\nexport function buildDashboard(node: DashboardNode): A2uiComponent[] {\n  return [{ id: node.id, component: &#039;Column&#039;, children: node.children }];\n}<\/code><\/pre>\n<p>The loader of the tile <code>BookedFlightsList<\/code> returns the booked flights in the shape the components bind to:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/dashboard-loaders.ts (simplified)\nexport async function loadBookedFlightsList(\n  node: BookedFlightsListNode,\n): Promise&lt;BookedFlightsListData&gt; {\n  const flights = [\n    { id: 1, route: &#039;Graz \u2192 Hamburg&#039;, meta: &#039;2026-11-20 \u00b7 On time&#039; },\n    { id: 2, route: &#039;Hamburg \u2192 Graz&#039;, meta: &#039;2026-11-20 \u00b7 On time&#039; },\n    { id: 516, route: &#039;Graz \u2192 Paris&#039;, meta: &#039;2026-11-21 \u00b7 On time&#039; },\n    { id: 50, route: &#039;Wien \u2192 Klagenfurt&#039;, meta: &#039;2026-11-24 \u00b7 Delayed by 30 min&#039; },\n  ];\n  const selected = node.maxRows ? flights.slice(0, node.maxRows) : flights;\n  const emptyMessage =\n    selected.length === 0 ? &#039;You have no booked flights.&#039; : &#039;&#039;;\n  return { flights: selected, emptyMessage };\n}<\/code><\/pre>\n<p>To keep the example simple, this version returns hardcoded flights. The example application loads the booked flights via HTTP instead and adds a weather forecast for the destination if <code>showWeather<\/code> is <code>true<\/code>.<\/p>\n<p>Its renderer returns the components shown above:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/dashboard-renderers.ts\nexport function buildBookedFlightsList(\n  node: BookedFlightsListNode,\n): A2uiComponent[] {\n  const id = node.id;\n  const bodyId = `${id}-body`;\n  [...]\n  const listId = `${id}-list`;\n  const rowId = `${id}-row`;\n  [...]\n\n  const components: A2uiComponent[] = [\n    { id, component: &#039;Card&#039;, child: bodyId },\n    [...]\n    {\n      id: listId,\n      component: &#039;Column&#039;,\n      children: { componentId: rowId, path: `\/${id}\/flights` },\n    },\n    [...]\n  ];\n\n  [...]\n  return components;\n}<\/code><\/pre>\n<p>The renderer does not receive the loaded data. The components it returns refer to the data via data binding. This keeps structure and data apart. Every renderer follows three rules:<\/p>\n<ul>\n<li>Exactly one of its components has the id of the node. This way, the references of the parent component remain valid.<\/li>\n<li>All other ids start with the id of the node. This keeps them unique across tiles.<\/li>\n<li>The data paths start with the id of the node, too, because the macro mechanism stores the result of <code>load<\/code> there.<\/li>\n<\/ul>\n<h3>Catalog and Request Processor<\/h3>\n<p>The function <code>toMacroCatalog<\/code> of the macro mechanism derives a catalog from the macros. It turns each Zod schema into a JSON schema and assigns it to the name of the macro. As in the <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-express-token-optimized-generative-uis\/\">previous article<\/a>, an <code>InMemoryCatalogProvider<\/code> loads this catalog for A2UI 0.9, and an <code>A2uiGenerator<\/code> creates the processor:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/dashboard-processor.ts\nimport {\n  A2uiGenerator,\n  CatalogConfig,\n  InMemoryCatalogProvider,\n} from &#039;@a2ui\/agent&#039;;\n\nimport { PROTOCOL_VERSION } from &#039;..\/a2ui\/a2ui-catalog-schema.js&#039;;\nimport { toMacroCatalog } from &#039;..\/a2ui\/simple-macros\/macro.js&#039;;\nimport { dashboardMacros } from &#039;.\/dashboard-macros.js&#039;;\n\nconst DASHBOARD_CATALOG_ID =\n  &#039;https:\/\/example.com\/catalogs\/flights42-dashboard-macros&#039;;\n\nexport const dashboardProcessor = createDashboardProcessor();\n\nfunction createDashboardProcessor() {\n  const catalogSchema = toMacroCatalog(dashboardMacros, DASHBOARD_CATALOG_ID);\n\n  const catalogProvider = new InMemoryCatalogProvider(\n    catalogSchema,\n    PROTOCOL_VERSION,\n  );\n  const dashboardCatalog = catalogProvider.load();\n\n  const catalogConfig = new CatalogConfig(dashboardCatalog);\n\n  const generator = new A2uiGenerator([catalogConfig]);\n\n  return generator.createProcessor({\n    supportedCatalogIds: [DASHBOARD_CATALOG_ID],\n  });\n}<\/code><\/pre>\n<p>The constant <code>PROTOCOL_VERSION<\/code> has the value <code>v0.9<\/code>. This time, the <code>CatalogConfig<\/code> gets no transformers, as the catalog only contains the macros and there is nothing to prune. Every new entry in <code>dashboardMacros<\/code> ends up in the catalog without further code.<\/p>\n<h3>Instructing the Agent<\/h3>\n<p>The prompt of the agent describes the macros as compact function signatures:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/agents\/dashboard-agent.prompt.ts\nexport const dashboardAgentPrompt = `\nYou compose the Flight42 dashboard.\n\nEach turn ends with **exactly one** renderDashboard tool call. Its\n&quot;express&quot; argument is a single statement wrapped in &lt;a2ui&gt; tags:\n\n&lt;a2ui&gt;\nroot = Dashboard([FlightsTable(&quot;Graz&quot;, &quot;Hamburg&quot;, maxRows=5), BookedFlightsList(showWeather=false)])\n&lt;\/a2ui&gt;\n\n[...]\n\nTile reference:\n\n- FlightsTable(from, to, maxRows?)\n  (default: 30)\n[...]\n- BookedFlightsList(showCheckInButton?, showWeather?, maxRows?)\n  (defaults: showCheckInButton true, showWeather true, maxRows no limit)\n[...]\n`;<\/code><\/pre>\n<p>I wrote this prompt by hand because the model only has to write a single entry per tile. Alternatively, the processor provides a ready-made prompt in its property <code>promptSnippet<\/code>, as in the previous article:<\/p>\n<pre><code class=\"language-ts\">import { dashboardProcessor } from &#039;..\/dashboard-dsl\/dashboard-processor.js&#039;;\n\nconst prompt = dashboardProcessor.promptSnippet;<\/code><\/pre>\n<p>However, this prompt explains the entire Express grammar, including data binding, validation, and events, which the dashboard doesn't need. At about 1,400 tokens, it is about 2.6 times as large as the entire handwritten prompt.<\/p>\n<p>The model passes its Express code to the tool <code>renderDashboard<\/code>. Its only parameter, <code>express<\/code>, takes the text including the tags <code>&lt;a2ui&gt;<\/code> and <code>&lt;\/a2ui&gt;<\/code>.<\/p>\n<h3>From Express to A2UI<\/h3>\n<p>The function <code>compileDashboard<\/code> translates the model's Express code into A2UI messages:<\/p>\n<pre><code class=\"language-ts\">\/\/ ai-server\/src\/mastra\/dashboard-dsl\/compile-dashboard.ts\nimport { randomUUID } from &#039;node:crypto&#039;;\n\nimport type { A2uiMessage } from &#039;@a2ui\/web_core\/v0_9&#039;;\nimport { A2UI_CATALOG_ID } from &#039;@flights42\/a2ui-catalog\/ticketing-extra-components&#039;;\n\nimport { expressToComponents } from &#039;..\/a2ui\/a2ui-express.js&#039;;\nimport { applyMacros } from &#039;..\/a2ui\/simple-macros\/apply-macros.js&#039;;\nimport { toA2uiMessages } from &#039;..\/a2ui\/simple-macros\/to-a2ui-messages.js&#039;;\nimport { dashboardMacros } from &#039;.\/dashboard-macros.js&#039;;\nimport { dashboardProcessor } from &#039;.\/dashboard-processor.js&#039;;\n\nexport interface CompiledDashboard {\n  surfaceId: string;\n  messages: A2uiMessage[];\n}\n\nexport async function compileDashboard(\n  express: string,\n): Promise&lt;CompiledDashboard&gt; {\n  const components = expressToComponents(dashboardProcessor, express);\n  const applied = await applyMacros(components, dashboardMacros);\n  const surfaceId = `dashboard-${randomUUID()}`;\n  const messages = toA2uiMessages(surfaceId, A2UI_CATALOG_ID, applied);\n  return { surfaceId, messages };\n}<\/code><\/pre>\n<p>The function <code>expressToComponents<\/code> parses the Express code with the function <code>compileExpress<\/code> from the <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-express-token-optimized-generative-uis\/\">previous article<\/a>. The processor checks every macro call against the schemas. The function returns the components of all <code>updateComponents<\/code> messages. For the dashboard above, these are the following macro nodes:<\/p>\n<pre><code class=\"language-json\">[\n  { &quot;id&quot;: &quot;root&quot;, &quot;component&quot;: &quot;Dashboard&quot;, &quot;children&quot;: [&quot;root_children_0&quot;, &quot;root_children_1&quot;, [...]] },\n  { &quot;id&quot;: &quot;root_children_0&quot;, &quot;component&quot;: &quot;BoardingPasses&quot;, &quot;count&quot;: 2 },\n  { &quot;id&quot;: &quot;root_children_1&quot;, &quot;component&quot;: &quot;BookedFlightsList&quot;, &quot;showWeather&quot;: false },\n  { &quot;id&quot;: &quot;root_children_2&quot;, &quot;component&quot;: &quot;FlightSearch&quot;, &quot;defaultFrom&quot;: &quot;Graz&quot;, &quot;defaultTo&quot;: &quot;Hamburg&quot; },\n  [...]\n]<\/code><\/pre>\n<p>The parser assigns the ids, and the renderers use them as prefixes.<\/p>\n<p>The function <code>applyMacros<\/code> of the macro mechanism runs the loaders of all macro nodes in parallel. After that, it calls the renderers and replaces each node with the returned components. It passes all other components through unchanged. The loaded data ends up under the id of the node, for instance under <code>\/root_children_1<\/code> for the booked flights. With the option <code>recursive<\/code>, the function also expands macros that other macros render. Like the <code>MacroExpander<\/code> of the Python SDK, it stops at a depth of 16.<\/p>\n<p>Finally, the function <code>toA2uiMessages<\/code> packs the result into A2UI messages. The message <code>createSurface<\/code> refers to the catalog of the client, not to the macro catalog, as no macros remain after <code>applyMacros<\/code>. Then <code>updateComponents<\/code> delivers the structure, and one <code>updateDataModel<\/code> message per macro delivers its data. For the booked flights, this message looks as follows:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;version&quot;: &quot;v0.9&quot;,\n  &quot;updateDataModel&quot;: {\n    &quot;surfaceId&quot;: &quot;dashboard-[...]&quot;,\n    &quot;path&quot;: &quot;\/root_children_1&quot;,\n    &quot;value&quot;: {\n      &quot;flights&quot;: [\n        { &quot;id&quot;: 1, &quot;route&quot;: &quot;Graz \u2192 Hamburg&quot;, &quot;meta&quot;: &quot;2026-11-20 \u00b7 On time&quot; },\n        { &quot;id&quot;: 2, &quot;route&quot;: &quot;Hamburg \u2192 Graz&quot;, &quot;meta&quot;: &quot;2026-11-20 \u00b7 On time&quot; },\n        { &quot;id&quot;: 516, &quot;route&quot;: &quot;Graz \u2192 Paris&quot;, &quot;meta&quot;: &quot;2026-11-21 \u00b7 On time&quot; },\n        { &quot;id&quot;: 50, &quot;route&quot;: &quot;Wien \u2192 Klagenfurt&quot;, &quot;meta&quot;: &quot;2026-11-24 \u00b7 Delayed by 30 min&quot; }\n      ],\n      &quot;emptyMessage&quot;: &quot;&quot;\n    }\n  }\n}<\/code><\/pre>\n<p>The template from above repeats the row for each of these four flights. This separation of structure and data makes the cache possible: the server caches the Express code of the model. For a recurring request, it runs <code>compileDashboard<\/code> again without calling the model, and the dashboard shows fresh data. The server sends the messages to the client as an AG-UI event.<\/p>\n<p>Once the TypeScript SDK supports macros, it can replace the macro mechanism.<\/p>\n<h2>What Macros Bring<\/h2>\n<p>As my article <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-i-made-my-a2ui-dashboard-300-times-faster\/\">How I Made My A2UI Dashboard 300 Times Faster<\/a> shows, the DSLs can bring a big performance gain: instead of about 40 seconds and more than 46,000 tokens, a dashboard takes about 2 seconds and less than 2,000 tokens. The variant with macros is just as fast and needs even fewer tokens.<\/p>\n<p>Beyond that, the already optimized application now relies on the A2UI project instead of a homegrown format: the DSL builds on A2UI and A2UI Express. The generic macro mechanism also tidies up the code. Each macro bundles the schema, the data loading, and the rendering of a tile. A new tile only needs a new macro, and the catalog picks it up automatically.<\/p>\n<p>Coarse-grained building blocks also make such dashboards feasible for local models. A few macro calls with flat arguments are easy even for smaller models. My article <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/optimizing-agentic-applications-for-local-models-patterns-for-speed-and-reliability\/\">Optimizing Agentic Applications for Local Models<\/a> shows how this approach eased moving the dashboard to Gemma 4. The combination of macros and A2UI Express makes this already optimized Gemma 4 version about 35 percent faster. The weaker the model, the more it benefits from such building blocks.<\/p>\n<p><div style=\"\nmargin: 8px 0;\npadding: 22px;\nborder: 1px solid #e5e7eb;\nborder-radius: 14px;\nbackground: #f8fafc;\n\"><\/p>\n<h3 style=\"margin-top:0\">Interested in production-ready Agentic UI architectures?<\/h3>\n<p>In my workshop, we dive into AG-UI, A2UI, MCP Apps, HITL patterns, and modern Angular architectures for real-world agentic systems.<\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/en\/training\/agentic-ai-with-angular-architecture-patterns\/\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/sujet-workshop-en.png\" width=\"400\" alt=\"Workshop: Agentic AI with Angular \u2013 AG-UI, A2UI, MCP Apps & HITL patterns\" style=\"cursor:pointer !important\"><\/a><\/p>\n<p><a style=\"cursor:pointer !important\" href=\"https:\/\/www.angulararchitects.io\/en\/training\/agentic-ai-with-angular-architecture-patterns\/\">See all details \u2192<\/a>\n<\/div>\n<\/p>\n<h2>Conclusion<\/h2>\n<p>A2UI macros bring the idea of an application-specific DSL into the A2UI SDKs. With a few calls, the model describes which building blocks it needs. The server resolves them deterministically into base components, and the client stays the same.<\/p>\n<p>The real lever is the level of abstraction. A few macro calls with fewer than 50 tokens replace a structure with about 1,300 tokens. This makes Generative UIs faster and cheaper and brings them to local models, too.<\/p>\n<p>So far, only the Python SDK supports macros. Until the TypeScript SDK catches up, a simple, generic mechanism does the job: the Express parser of the SDK validates the macro calls, and the function <code>applyMacros<\/code> loads the data and replaces the macros with base components. Once the SDK supports macros, it can take over this part.<\/p>\n<h2>FAQ<\/h2>\n<p><strong>What are A2UI macros?<\/strong><br \/>\nMacros are composite components that developers define on the server. The model calls them like regular components, and the server resolves them into base components before sending them. The client only needs to know the base components.<\/p>\n<p><strong>Which SDKs support A2UI macros?<\/strong><br \/>\nWhen I wrote this article, only the Python SDK for A2UI supported macros. For TypeScript, the article shows an interim solution made of the Express parser of <code>@a2ui\/agent<\/code> and a simple, generic macro mechanism of about 170 lines.<\/p>\n<p><strong>Do A2UI macros support A2UI 1.0?<\/strong><br \/>\nYes. The Python SDK supports macros for A2UI 0.9 and A2UI 1.0.<\/p>\n<p><strong>How many tokens do A2UI macros save?<\/strong><br \/>\nIn the example, the Express code with macros takes fewer than 50 tokens, and the A2UI structure resulting from it about 1,300. Compared to a DSL made of JSON objects, mainly the input tokens dropped, by about 40 percent.<\/p>\n<p><strong>Do A2UI macros work with local models?<\/strong><br \/>\nCompact building blocks with a few flat arguments are easy even for smaller open-weight models. With such a DSL, the dashboard of the example application moved to Gemma 4, which you can run on your own hardware.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is post 12 of 12 in the series &ldquo;Agentic UI with Angular&rdquo; Understanding AG-UI: The Standard for Agentic User Interfaces AG-UI in Practice: The SDK for TypeScript AG-UI End to End: Connecting Server and Client Agentic UI with Angular, CopilotKit, and AG-UI A2UI in Angular: How AI Generates Dynamic UIs at Runtime Integrating A2UI [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":35353,"comment_status":"open","ping_status":"open","sticky":true,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-35359","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","post_series-agentic-angular"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models - ANGULARarchitects<\/title>\n<meta name=\"description\" content=\"A2UI macros bundle whole UI building blocks into a single call. This article shows how macros work with A2UI Express, how to implement them with TypeScript today, and what coarse-grained building blocks bring for speed and cost with local and cloud models.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"A2UI macros bundle whole UI building blocks into a single call. This article shows how macros work with A2UI Express, how to implement them with TypeScript today, and what coarse-grained building blocks bring for speed and cost with local and cloud models.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2026-11-12T00:22:27+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-12T00:52:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/social-sujet-2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2400\" \/>\n\t<meta property=\"og:image:height\" content=\"1256\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Manfred Steyer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"TechArticle\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\"},\"author\":{\"name\":\"Manfred Steyer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\"},\"headline\":\"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models\",\"datePublished\":\"2026-11-12T00:22:27+00:00\",\"dateModified\":\"2026-11-12T00:52:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\"},\"wordCount\":2145,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\",\"name\":\"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png\",\"datePublished\":\"2026-11-12T00:22:27+00:00\",\"dateModified\":\"2026-11-12T00:52:11+00:00\",\"description\":\"A2UI macros bundle whole UI building blocks into a single call. This article shows how macros work with A2UI Express, how to implement them with TypeScript today, and what coarse-grained building blocks bring for speed and cost with local and cloud models.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png\",\"width\":1920,\"height\":1080,\"caption\":\"A2UI macros expand a compact dashboard description into base components and a flight dashboard.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\",\"name\":\"Manfred Steyer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models - ANGULARarchitects","description":"A2UI macros bundle whole UI building blocks into a single call. This article shows how macros work with A2UI Express, how to implement them with TypeScript today, and what coarse-grained building blocks bring for speed and cost with local and cloud models.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/","og_locale":"en_US","og_type":"article","og_title":"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models - ANGULARarchitects","og_description":"A2UI macros bundle whole UI building blocks into a single call. This article shows how macros work with A2UI Express, how to implement them with TypeScript today, and what coarse-grained building blocks bring for speed and cost with local and cloud models.","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/","og_site_name":"ANGULARarchitects","article_published_time":"2026-11-12T00:22:27+00:00","article_modified_time":"2026-11-12T00:52:11+00:00","og_image":[{"width":2400,"height":1256,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/social-sujet-2.png","type":"image\/png"}],"author":"Manfred Steyer","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Manfred Steyer","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"TechArticle","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/"},"author":{"name":"Manfred Steyer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a"},"headline":"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models","datePublished":"2026-11-12T00:22:27+00:00","dateModified":"2026-11-12T00:52:11+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/"},"wordCount":2145,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/","name":"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png","datePublished":"2026-11-12T00:22:27+00:00","dateModified":"2026-11-12T00:52:11+00:00","description":"A2UI macros bundle whole UI building blocks into a single call. This article shows how macros work with A2UI Express, how to implement them with TypeScript today, and what coarse-grained building blocks bring for speed and cost with local and cloud models.","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/11\/hero-1.png","width":1920,"height":1080,"caption":"A2UI macros expand a compact dashboard description into base components and a flight dashboard."},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/fewer-tokens-faster-uis-a2ui-macros-for-local-and-cloud-models\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Fewer Tokens, Faster UIs: A2UI Macros for Local and Cloud Models"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a","name":"Manfred Steyer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","caption":"Manfred Steyer"}}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/35359","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=35359"}],"version-history":[{"count":2,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/35359\/revisions"}],"predecessor-version":[{"id":35366,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/35359\/revisions\/35366"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/35353"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=35359"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=35359"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=35359"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}