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.zipInstalls 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.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
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.
- No
format!()in writer output. Use sequentialwriter.write()calls.format!allocates a temporaryStringevery invocation. - No
.to_string()onCow. WriteCow<str>directly to avoid defeating zero-copy. - No
collect::<Vec<_>>()on splits. Iteratepath.split('.')directly. Collecting allocates aVecfor sequential access. - No redundant scans. Use
first_part.len() == path.len()instead ofpath.contains('.'). - No
String::from(ch)in escape loops. Usech.encode_utf8(&mut buf)with a[u8; 4]stack buffer, or batch contiguous safe chars into a single write. - No
format!in hex parsing. Use direct arithmetic ((hi_nibble << 4) | lo_nibble) instead ofu8::from_str_radix(&format!(...)). - No per-request template re-parsing. Pre-parse at protocol load time and reuse.
- No silent
unwrap_ordefaults. Ifbinding_stack.pop()returnsNone, that's a protocol error - propagate it, don't mask it. - 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. Seeskills/diagnostics/SKILL.md§7.
Rust - memory rules
- No cloning large state trees. Use
evaluate_with_resolverwith a closure. Cloning duplicates the entire JSON tree in memory. - No cloning HashMaps for scope. Save/restore only the overwritten key on loop iteration. A HashMap clone copies every entry.
- No cloning
Stringvalues for read-only access. Uses.as_str()forValue::Stringbranches; only create owned strings forNumber/Boolvia a scratch buffer orCow. - Pre-allocate buffers. Use
Vec::with_capacity/String::with_capacitywhen size is known or estimable. For HTML output, 4096 bytes is a reasonable starting point. - Prefer
&strand slices over owned types. Pass by reference when the callee only reads. Move clone decisions to the caller. - Use
Cow<'_, str>when a value is sometimes borrowed, sometimes owned. Avoids unconditional allocation. - No deep-cloning protocol or state per request. Use
Arc<T>with clone-on-write or snapshot swapping. - 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
- Single-pass hydration. The framework walks the DOM once to connect all bindings. No multi-pass scanning.
- Path-indexed targeted updates. When an
@observablechanges, only bindings referencing that property are visited - not the entire template. - DOM cloning over innerHTML. Use
cloneNode(true)from cached template fragments. Never useinnerHTMLfor component creation. - Delegated events. One listener per event type on the shadow root, not one closure per element. Reduces listener count by orders of magnitude.
- Microtask coalescing. Multiple property changes within the same synchronous block batch into a single DOM update via
queueMicrotask. - Cursor-based repeat reconciliation.
<for>block updates use a diff algorithm that only callsinsertBeforeon nodes that actually moved. Append/prepend/remove are O(1). - No
for..inon objects. UseObject.keys()with an indexedforloop — faster and prototype-safe without needingObject.hasOwn. Applies tosetState,setInitialState, and any code iterating user-provided objects.
Memory
- No framework in the GC. Minimize object allocations during reactive updates. Reuse binding objects, don't recreate them.
- Template cache is
WeakMap-keyed. Parsed template DOMs are cached per metadata object. When metadata is released (e.g., viaRouter.gc()), the cache entry becomes GC-eligible. - No per-update array allocations. Avoid
.filter(),.map(),.slice()in the update hot path. Use index-based iteration. - 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.
- 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
- Server does route matching. The client does not re-match routes. The server returns the matched
chainarray; the client diffs old vs new and mounts only changed components. - Lazy loading via dynamic import. Route component JS is fetched only on first navigation to that route.
- Chain diffing, not full remount. Navigating between sibling routes preserves parent components. Only the changed level is remounted.
- No
for..inon objects. UseObject.keys()with an indexedforloop.for..inwalks the prototype chain (slow) and requires anObject.hasOwnguard to be safe —Object.keysis 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
- Release unused templates.
Router.gc()clears cached component templates for routes the user hasn't visited recently. Active route components are never released. - Inventory bitmask prevents duplicate downloads. The server tracks which component templates the client already has via a bitmask. Re-navigation never re-sends templates.
- 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.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| perf (this skill) | 0 | 1mo | Review | Advanced |
| telegram-mini-app | 62 | 6mo | Review | Advanced |
| bullmq-specialist | 25 | 6mo | No flags | Intermediate |
| shopify-development | 12 | 6mo | Review | Intermediate |
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.
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.
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"
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.
shopify-apps
alinaqi
Shopify app development - Remix, Admin API, checkout extensions
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.