{"id":50372401,"url":"https://github.com/husseinAbdElaziz/angular-scan","last_synced_at":"2026-06-01T10:00:39.924Z","repository":{"id":343774072,"uuid":"1176193282","full_name":"husseinAbdElaziz/angular-scan","owner":"husseinAbdElaziz","description":"Automatically detects and highlights Angular components that are re-rendering","archived":false,"fork":false,"pushed_at":"2026-03-31T17:49:51.000Z","size":2174,"stargazers_count":5,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-22T00:17:29.930Z","etag":null,"topics":["angular","change-detection","performance","rendering","ui"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/angular-scan","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/husseinAbdElaziz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-08T18:37:28.000Z","updated_at":"2026-03-22T22:36:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/husseinAbdElaziz/angular-scan","commit_stats":null,"previous_names":["husseinabdelaziz/angular-scan"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/husseinAbdElaziz/angular-scan","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/husseinAbdElaziz%2Fangular-scan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/husseinAbdElaziz%2Fangular-scan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/husseinAbdElaziz%2Fangular-scan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/husseinAbdElaziz%2Fangular-scan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/husseinAbdElaziz","download_url":"https://codeload.github.com/husseinAbdElaziz/angular-scan/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/husseinAbdElaziz%2Fangular-scan/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33769492,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","change-detection","performance","rendering","ui"],"created_at":"2026-05-30T08:00:26.065Z","updated_at":"2026-06-01T10:00:39.918Z","avatar_url":"https://github.com/husseinAbdElaziz.png","language":"TypeScript","funding_links":[],"categories":["Development Utilities"],"sub_categories":["Debugging"],"readme":"# angular-scan\n\nAutomatically detects and highlights Angular components that are re-rendering — the Angular equivalent of [react-scan](https://github.com/aidenybai/react-scan).\n\n- **Yellow flash** — component was checked and its DOM changed (normal re-render)\n- **Red flash** — component was checked but its DOM did **not** change (unnecessary render)\n- **Counter badge** — cumulative render count on each component host element\n- **Toolbar HUD** — draggable floating panel with live stats, runtime toggles, flash-duration slider, reset, and a per-component inspector\n\nZero overhead in production — the entire library is tree-shaken when `isDevMode()` returns `false`.\n\nWorks with both **zone.js** and **zoneless** Angular applications.\n\n![angular-scan demo](https://raw.githubusercontent.com/husseinAbdElaziz/angular-scan/main/demo.gif)\n\n👉 **[Live Demo](https://husseinabdelaziz.github.io/angular-scan)**\n\n---\n\n## Installation\n\n```bash\nnpm install angular-scan --save-dev\n```\n\n---\n\n## Usage\n\n### Provider-based (recommended)\n\nAdd `provideAngularScan()` to your application providers:\n\n```ts\n// app.config.ts\nimport { ApplicationConfig } from '@angular/core';\nimport { provideAngularScan } from 'angular-scan';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideAngularScan(),\n  ],\n};\n```\n\n### Imperative API\n\nFor micro-frontends or apps where you can't modify providers, call `scan()` before `bootstrapApplication`:\n\n```ts\n// main.ts\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { scan } from 'angular-scan';\nimport { AppComponent } from './app/app.component';\nimport { appConfig } from './app/app.config';\n\nscan();\nbootstrapApplication(AppComponent, appConfig);\n```\n\n`scan()` returns a teardown function:\n\n```ts\nconst stop = scan();\n// later...\nstop(); // removes overlay and stops tracking\n```\n\n---\n\n## Options\n\n```ts\nprovideAngularScan({\n  enabled: true,         // set false to disable entirely (default: true)\n  flashDurationMs: 500,  // how long the flash animation lasts in ms (default: 500)\n  showBadges: true,      // show render count badges on host elements (default: true)\n  showToolbar: true,     // show the floating toolbar HUD (default: true)\n});\n```\n\nThe same options are accepted by `scan()`.\n\n---\n\n## How it works\n\nAngular exposes `window.ng.ɵsetProfiler()` in development mode — the same hook used by Angular DevTools in Chrome. `angular-scan` registers a profiler callback to intercept every change detection cycle:\n\n1. **`ChangeDetectionStart`** — a `MutationObserver` begins recording all DOM mutations\n2. **`ChangeDetectionSyncStart/End`** — gates which `TemplateUpdateStart` events count as real renders (excludes the dev-mode `checkNoChanges` pass)\n3. **`TemplateUpdateStart`** — the exact component instance being checked is captured\n4. **`ChangeDetectionEnd`** — `MutationObserver.takeRecords()` flushes synchronously; each captured instance is mapped to its host element via `ng.getHostElement()`; components whose subtree had DOM mutations are marked as **renders**, the rest as **unnecessary renders**\n\nAll Angular signal writes are deferred via `queueMicrotask()` to avoid triggering a new CD cycle from inside the profiler callback.\n\nThe canvas overlay (`position: fixed`, full viewport, `pointer-events: none`) uses a `requestAnimationFrame` loop to draw and fade rectangles over component host elements. The toolbar is created via `createComponent()` and attached to `ApplicationRef` outside the normal component tree, so its own renders are excluded from tracking.\n\n---\n\n## Interpreting the output\n\n| Signal | Meaning | Common cause |\n|--------|---------|-------------|\n| Yellow flash | Component re-rendered (DOM changed) | Normal update — signal/input changed |\n| Red flash | Component checked but DOM unchanged | Parent uses `Default` CD strategy; child is `OnPush` with no changed inputs |\n| High wasted count on a component | It's being checked unnecessarily on every tick | Wrap it in `OnPush`; ensure parent isn't `Default` CD |\n| Counter badge turns red | More unnecessary than necessary renders | Same as above — component is `OnPush` but still gets walked |\n\n---\n\n## Toolbar HUD\n\nThe floating toolbar mounts in the bottom-right corner and can be dragged anywhere on screen. The panel is clamped inside the viewport on drag, resize, and when expanding sections near a screen edge.\n\n**Header**\n\n| Button | Action |\n|--------|--------|\n| ⏸ / ▶ | Pause or resume scanning |\n| ⚙ | Open the settings panel |\n| ▲ / ▼ | Expand or collapse the per-component inspector |\n\nThe header itself is the drag handle — grab anywhere outside the buttons to move the panel.\n\n**Stats**\n\n- **CHECKS** — total components checked since the last reset\n- **WASTED** — total unnecessary renders (turns red when \u003e 0)\n\n**Settings panel** (⚙)\n\n- **Enable scanning** — mirrors the header pause button\n- **Flash overlay** — show/hide the canvas flashes\n- **Render badges** — show/hide the counter badges on host elements\n- **Flash duration** — slider, 100 ms – 2000 ms\n- **↺ Reset stats** — clears all render counts and the inspector list\n\n**Inspector** (▲) lists each tracked component with its name, total render count, and a `nW` wasted suffix. Rows whose most recent render was \"unnecessary\" are highlighted red.\n\nPass `showToolbar: false` to disable the HUD entirely while keeping flashes and badges.\n\n---\n\n## Requirements\n\n- Angular **≥ 20**\n- Must be used in **development mode** (`ng serve` / `ng build --configuration development`)\n- The Angular debug APIs (`window.ng`) are only available in dev mode — `angular-scan` is silently disabled otherwise\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FhusseinAbdElaziz%2Fangular-scan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FhusseinAbdElaziz%2Fangular-scan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FhusseinAbdElaziz%2Fangular-scan/lists"}