Performance optimization rules focusing on memory and throughput for Rust and WebUI frameworks.

Install

mkdir -p .claude/skills/perf && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/18721" && unzip -o skill.zip -d .claude/skills/perf && rm skill.zip

Installs to .claude/skills/perf

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Speed and memory performance rules for Rust crates, webui-framework, and webui-router.
86 charsno explicit “when” trigger
Advanced

Key capabilities

  • Optimize Rust code for throughput and memory
  • Reduce memory allocations in Rust and TypeScript
  • Improve WebUI framework hydration and updates
  • Optimize WebUI router for client-side SPA
  • Measure performance using benchmarks

How it works

The skill provides specific rules for Rust and TypeScript to minimize allocations, optimize string handling, and improve iteration patterns, along with methods for benchmarking.

Inputs & outputs

You give it
Performance-sensitive code changes in Rust or TypeScript
You get back
Code with improved throughput and reduced memory usage

When to use perf

  • Improve Rust application speed
  • Reduce memory allocations
  • Optimize webui framework performance
  • Debug performance regressions

About this skill

Performance

WebUI's value proposition is speed and low memory usage. Every change to core Rust crates, @microsoft/webui-framework, or @microsoft/webui-router must be evaluated through two lenses: throughput (how fast) and memory (how little).

Server memory is not cheap. Client memory is not unlimited. Every allocation that can be avoided is a win on both sides.

Use this skill when modifying any performance-sensitive code across the stack.

Rust - speed rules

These apply to webui-handler, webui-state, webui-expressions, webui-parser, webui-protocol, webui-ffi, and webui-node.

  1. No format!() in writer output. Use sequential writer.write() calls. format! allocates a temporary String every invocation.
  2. No .to_string() on Cow. Write Cow<str> directly to avoid defeating zero-copy.
  3. No collect::<Vec<_>>() on splits. Iterate path.split('.') directly. Collecting allocates a Vec for sequential access.
  4. No redundant scans. Use first_part.len() == path.len() instead of path.contains('.').
  5. No String::from(ch) in escape loops. Use ch.encode_utf8(&mut buf) with a [u8; 4] stack buffer, or batch contiguous safe chars into a single write.
  6. No format! in hex parsing. Use direct arithmetic ((hi_nibble << 4) | lo_nibble) instead of u8::from_str_radix(&format!(...)).
  7. No per-request template re-parsing. Pre-parse at protocol load time and reuse.
  8. No silent unwrap_or defaults. If binding_stack.pop() returns None, that's a protocol error - propagate it, don't mask it.
  9. Keep cold error/diagnostic construction off the hot path. Mark error builders #[cold]/#[inline(never)] so they don't inline into hot functions and perturb their code layout. Adding cold error code with no hot-path work has regressed parse benches ~4-5% purely via layout - keep per-element fast-paths inlinable and push the cold fallback out-of-line. See skills/diagnostics/SKILL.md §7.

Rust - memory rules

  1. No cloning large state trees. Use evaluate_with_resolver with a closure. Cloning duplicates the entire JSON tree in memory.
  2. No cloning HashMaps for scope. Save/restore only the overwritten key on loop iteration. A HashMap clone copies every entry.
  3. No cloning String values for read-only access. Use s.as_str() for Value::String branches; only create owned strings for Number/Bool via a scratch buffer or Cow.
  4. Pre-allocate buffers. Use Vec::with_capacity / String::with_capacity when size is known or estimable. For HTML output, 4096 bytes is a reasonable starting point.
  5. Prefer &str and slices over owned types. Pass by reference when the callee only reads. Move clone decisions to the caller.
  6. Use Cow<'_, str> when a value is sometimes borrowed, sometimes owned. Avoids unconditional allocation.
  7. No deep-cloning protocol or state per request. Use Arc<T> with clone-on-write or snapshot swapping.
  8. Cap memory for untrusted inputs. File reads during discovery must have size limits. A 100MB HTML file should not cause OOM.

TypeScript - @microsoft/webui-framework rules

These apply to packages/webui-framework (the client-side Web Component runtime).

Speed

  1. Single-pass hydration. The framework walks the DOM once to connect all bindings. No multi-pass scanning.
  2. Path-indexed targeted updates. When an @observable changes, only bindings referencing that property are visited - not the entire template.
  3. DOM cloning over innerHTML. Use cloneNode(true) from cached template fragments. Never use innerHTML for component creation.
  4. Delegated events. One listener per event type on the shadow root, not one closure per element. Reduces listener count by orders of magnitude.
  5. Microtask coalescing. Multiple property changes within the same synchronous block batch into a single DOM update via queueMicrotask.
  6. Cursor-based repeat reconciliation. <for> block updates use a diff algorithm that only calls insertBefore on nodes that actually moved. Append/prepend/remove are O(1).
  7. No for..in on objects. Use Object.keys() with an indexed for loop — faster and prototype-safe without needing Object.hasOwn. Applies to setState, setInitialState, and any code iterating user-provided objects.

Memory

  1. No framework in the GC. Minimize object allocations during reactive updates. Reuse binding objects, don't recreate them.
  2. Template cache is WeakMap-keyed. Parsed template DOMs are cached per metadata object. When metadata is released (e.g., via Router.gc()), the cache entry becomes GC-eligible.
  3. No per-update array allocations. Avoid .filter(), .map(), .slice() in the update hot path. Use index-based iteration.
  4. Strip SSR markers after hydration. Comment nodes used as markers are removed from the DOM once wiring is complete - they don't persist as memory overhead.
  5. Scope frames are stack-allocated. <for> loop item variables use a linked-list scope chain, not cloned Maps or Objects.

TypeScript - @microsoft/webui-router rules

These apply to packages/webui-router (the client-side SPA router).

Speed

  1. Server does route matching. The client does not re-match routes. The server returns the matched chain array; the client diffs old vs new and mounts only changed components.
  2. Lazy loading via dynamic import. Route component JS is fetched only on first navigation to that route.
  3. Chain diffing, not full remount. Navigating between sibling routes preserves parent components. Only the changed level is remounted.
  4. No for..in on objects. Use Object.keys() with an indexed for loop. for..in walks the prototype chain (slow) and requires an Object.hasOwn guard to be safe — Object.keys is both faster and prototype-safe in one call:
    // ✗ Bad: slow, prototype-unsafe without guard
    for (const key in obj) { ... }
    
    // ✗ Still bad: correct but slower than Object.keys
    for (const key in obj) {
      if (Object.hasOwn(obj, key)) { ... }
    }
    
    // ✓ Good: fast, prototype-safe, no guard needed
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) { ... }
    

Memory

  1. Release unused templates. Router.gc() clears cached component templates for routes the user hasn't visited recently. Active route components are never released.
  2. Inventory bitmask prevents duplicate downloads. The server tracks which component templates the client already has via a bitmask. Re-navigation never re-sends templates.
  3. Minimal state per navigation. Route-scoped state means the JSON partial contains only what the active route needs, not the full app state.

Measuring

Server benchmarks

cargo bench -p microsoft-webui --bench contact_book_bench          # full run
cargo bench -p microsoft-webui --bench contact_book_bench -- --test # quick validation
cargo xtask bench all                                               # all Rust crates
cargo xtask bench node-addon                                        # Node/V8/N-API boundary

Compare Render/1000 P50 before and after. For Node changes, save with --save-baseline before and compare with --baseline before. Verify output Bytes is unchanged (same HTML = correct behavior).

Client performance

window.addEventListener('webui:hydration-complete', () => {
  for (const entry of performance.getEntriesByType('measure')) {
    if (entry.name.startsWith('webui:hydrate:')) {
      console.log(`${entry.name}: ${entry.duration.toFixed(1)}ms`);
    }
  }
});

What to report

When making a performance-related change, report:

  • Before/after benchmark numbers (P50 latency, throughput)
  • Allocation count delta if measurable
  • Output size unchanged (proves correctness)
  • Memory profile for memory-related changes (heap snapshots, RSS delta)

When not to use it

  • When `format!()` is used in Rust writer output
  • When `.to_string()` is used on `Cow` in Rust
  • When `collect::<Vec<_>>()` is used on splits in Rust

Limitations

  • Requires avoiding `format!()` in Rust writer output
  • Requires avoiding `.to_string()` on `Cow` in Rust
  • Requires avoiding `collect::<Vec<_>>()` on splits in Rust

How it compares

This skill offers detailed, language-specific performance optimization rules and measurement techniques for Rust and TypeScript, unlike general performance advice.

Compared to similar skills

perf side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
perf (this skill)01moReviewAdvanced
telegram-mini-app626moReviewAdvanced
bullmq-specialist256moNo flagsIntermediate
shopify-development126moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by microsoft

View all by microsoft

You might also like

telegram-mini-app

davila7

Expert in building Telegram Mini Apps (TWA) - web apps that run inside Telegram with native-like experience. Covers the TON ecosystem, Telegram Web App API, payments, user authentication, and building viral mini apps that monetize. Use when: telegram mini app, TWA, telegram web app, TON app, mini app.

62163

bullmq-specialist

davila7

BullMQ expert for Redis-backed job queues, background processing, and reliable async execution in Node.js/TypeScript applications. Use when: bullmq, bull queue, redis queue, background job, job queue.

2595

shopify-development

davila7

Build Shopify apps, extensions, themes using GraphQL Admin API, Shopify CLI, Polaris UI, and Liquid. TRIGGER: "shopify", "shopify app", "checkout extension", "admin extension", "POS extension", "shopify theme", "liquid template", "polaris", "shopify graphql", "shopify webhook", "shopify billing", "app subscription", "metafields", "shopify functions"

1299

rust-errors

EpicenterHQ

Rust to TypeScript error handling patterns for Tauri apps. Use when defining Rust errors that will be passed to TypeScript, handling Tauri command errors, or creating discriminated union error types.

521

shopify-apps

alinaqi

Shopify app development - Remix, Admin API, checkout extensions

19

hula-skill

HuLaSpark

HuLa project skill for frontend (Vue 3 + Vite + UnoCSS + Naive UI/Vant), backend (Tauri v2 + Rust + SeaORM/SQLite), full-stack flows, and build/release work. Use when the user mentions hula or HuLa or requests changes in this repository; after triggering, ask which scope (frontend/backend/fullstack/build-release) to enable.

34

Search skills

Search the agent skills registry