This commit is contained in:
phaichayon
2026-07-02 10:20:45 +07:00
parent f7c9037ba4
commit bca8d3f7e0
147 changed files with 29838 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Components,Use standalone components,Angular 17+ default; no NgModule needed,Standalone components for all new code,NgModule-based components for new projects,"@Component({ standalone: true imports: [CommonModule] })","@NgModule({ declarations: [MyComp] })",High,https://angular.dev/guide/components/importing
2,Components,Use signals for state,Signals are Angular's reactive primitive for fine-grained reactivity,Signals for component state over class properties,Mutable class properties without signals,"count = signal(0); increment() { this.count.update(v => v + 1) }","count = 0; increment() { this.count++ }",High,https://angular.dev/guide/signals
3,Components,Use @if/@for/@switch control flow,Built-in control flow syntax replaces *ngIf/*ngFor directives,@if and @for in templates,*ngIf and *ngFor structural directives,"@if (isLoggedIn) { <Dashboard /> } @else { <Login /> }","<div *ngIf=""isLoggedIn""><Dashboard /></div>",High,https://angular.dev/guide/templates/control-flow
4,Components,Use input() and output() signals,Signal-based inputs/outputs replace @Input()/@Output() decorators,input() and output() for component API,@Input() and @Output() decorators,"name = input<string>(); clicked = output<void>()","@Input() name: string; @Output() clicked = new EventEmitter()",High,https://angular.dev/guide/components/inputs
5,Components,Use content projection,ng-content for flexible component composition,ng-content with select for named slots,Rigid templates that can't be customized,"<ng-content select=""[header]"" /> <ng-content />","<div class=""header"">{{ title }}</div>",Medium,https://angular.dev/guide/components/content-projection
6,Components,Keep components small,Single responsibility; components should do one thing,Extract sub-components when template exceeds 50 lines,Monolithic components handling multiple concerns,"<UserAvatar /> <UserDetails /> <UserActions />",One 300-line component template,Medium,https://angular.dev/guide/components
7,Components,Use OnPush change detection,Reduces re-renders by only checking on input changes or signal updates,OnPush for all components,Default change detection strategy,"changeDetection: ChangeDetectionStrategy.OnPush","changeDetection: ChangeDetectionStrategy.Default",High,https://angular.dev/guide/components/lifecycle
8,Components,Avoid direct DOM manipulation,Use renderer or ElementRef sparingly; prefer template bindings,Template bindings and Angular directives,Direct document.querySelector or innerHTML,"[class.active]=""isActive""","this.el.nativeElement.classList.add('active')",High,https://angular.dev/guide/components/host-elements
9,Routing,Lazy load feature routes,Load route chunks on demand to reduce initial bundle,loadComponent() for all feature routes,Eager-loaded routes in app config,"{ path: 'admin' loadComponent: () => import('./admin/admin.component') }","{ path: 'admin' component: AdminComponent }",High,https://angular.dev/guide/routing/lazy-loading
10,Routing,Use route guards with functional API,Protect routes with canActivate/canMatch functional guards,Functional guards returning boolean or UrlTree,Class-based guards with CanActivate interface,"canActivate: [() => inject(AuthService).isLoggedIn()]","canActivate: [AuthGuard]",High,https://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access
11,Routing,Use route resolvers for data,Pre-fetch data before route activation using resolve,ResolveFn for route data,Fetching data in ngOnInit causing flash of empty state,"resolve: { user: () => inject(UserService).getUser() }",Fetch in ngOnInit with loading state flickering,Medium,https://angular.dev/guide/routing/common-router-tasks#resolve
12,Routing,Type route params with inject,Use inject(ActivatedRoute) with signals or toSignal,Typed route params via ActivatedRoute,Untyped route.snapshot.params string access,"const id = toSignal(route.paramMap.pipe(map(p => p.get('id'))))","const id = this.route.snapshot.params['id']",Medium,https://angular.dev/api/router/ActivatedRoute
13,Routing,Use nested routes for layouts,Compose shared layouts using router-outlet nesting,Nested routes with shared layout components,Duplicating layout code across routes,"{ path: 'app' component: ShellComponent children: [...] }",Duplicate header/sidebar in each route component,Medium,https://angular.dev/guide/routing/router-tutorial-toh#child-route-configuration
14,Routing,Configure preloading strategies,Preload lazy modules in background after initial load,PreloadAllModules or custom strategy,No preloading causing delayed navigation,"provideRouter(routes withPreloading(PreloadAllModules))","provideRouter(routes)",Low,https://angular.dev/api/router/PreloadAllModules
15,State,Use signals for local state,Signals provide synchronous reactive state without RxJS overhead,signal() for component-local reactive state,BehaviorSubject for simple local state,"const items = signal<Item[]>([]); addItem(i: Item) { this.items.update(arr => [...arr i]) }","items$ = new BehaviorSubject<Item[]>([])",High,https://angular.dev/guide/signals
16,State,Use computed() for derived state,Lazily evaluated derived values that update when dependencies change,computed() for values derived from other signals,Duplicated state or manual sync,"readonly total = computed(() => this.items().reduce((s i) => s + i.price 0))","this.total = this.items.reduce(...) // called manually",High,https://angular.dev/guide/signals#computed-signals
17,State,Use effect() carefully,Effects run side effects when signals change; avoid overuse,effect() for side effects like logging or localStorage sync,effect() for deriving state (use computed instead),"effect(() => localStorage.setItem('cart' JSON.stringify(this.cart())))","effect(() => { this.total.set(this.items().length) })",Medium,https://angular.dev/guide/signals#effects
18,State,Use NgRx Signal Store for complex state,NgRx Signal Store is the modern lightweight state management for Angular,@ngrx/signals SignalStore for feature state,Full NgRx reducer/action/effect boilerplate for simple state,"const Store = signalStore(withState({ count: 0 }) withMethods(s => ({ increment: () => patchState(s { count: s.count() + 1 }) })))","createReducer(on(increment state => ({ ...state count: state.count + 1 })))",Medium,https://ngrx.io/guide/signals
19,State,Inject services for shared state,Services with signals share state across components without a store,Injectable service with signals for cross-component state,Prop drilling or @Input chains for shared state,"@Injectable({ providedIn: 'root' }) class CartService { items = signal<Item[]>([]) }","@Input() cartItems passed through 4 component levels",Medium,https://angular.dev/guide/di/creating-injectable-service
20,State,Avoid mixing RxJS and signals unnecessarily,Use toSignal() to bridge RxJS into signal world at the boundary,toSignal() to convert observable to signal at component edge,Subscribing in components and storing in signal manually,"readonly user = toSignal(this.userService.user$)","this.userService.user$.subscribe(u => this.user.set(u))",Medium,https://angular.dev/guide/rxjs-interop
21,Forms,Use typed reactive forms,FormGroup/FormControl with explicit generics for compile-time safety,FormBuilder with typed controls,Untyped FormControl or any casts,"fb.group<LoginForm>({ email: fb.control('') password: fb.control('') })","new FormGroup({ email: new FormControl(null) })",High,https://angular.dev/guide/forms/typed-forms
22,Forms,Use reactive forms over template-driven,Reactive forms scale better and are fully testable,ReactiveFormsModule for all non-trivial forms,FormsModule with ngModel for complex forms,"<input [formControl]=""emailControl"" />","<input [(ngModel)]=""email"" />",Medium,https://angular.dev/guide/forms/reactive-forms
23,Forms,Write custom validators as functions,Functional validators are composable and tree-shakeable,ValidatorFn functions for custom validation,Class-based validators implementing Validator interface,"const noSpaces: ValidatorFn = ctrl => ctrl.value?.includes(' ') ? { noSpaces: true } : null","class NoSpacesValidator implements Validator { validate(c) {} }",Medium,https://angular.dev/guide/forms/form-validation#custom-validators
24,Forms,Use updateOn for performance,Control when validation runs to avoid per-keystroke validation overhead,updateOn: 'blur' or 'submit' for expensive validators,Default updateOn: 'change' for async validators,"fb.control('' { updateOn: 'blur' validators: [Validators.email] })","fb.control('' [Validators.email]) // validates on every key",Low,https://angular.dev/api/forms/AbstractControl#updateOn
25,Forms,Use FormArray for dynamic fields,FormArray manages variable-length lists of controls,FormArray for add/remove field scenarios,Manually tracking index-based controls,"get items(): FormArray { return this.form.get('items') as FormArray }","items: [FormControl] managed outside form",Medium,https://angular.dev/guide/forms/reactive-forms#using-the-formarray-class
26,Forms,Display validation errors clearly,Use form control touched and dirty states to show errors at the right time,Show errors after field is touched,Show all errors on page load,"@if (email.invalid && email.touched) { <span>Invalid email</span> }","@if (email.invalid) { <span>Invalid email</span> }",Medium,https://angular.dev/guide/forms/form-validation
27,Performance,Apply OnPush to all components,OnPush + signals eliminates most unnecessary change detection cycles,OnPush change detection everywhere,Default strategy which checks entire tree on every event,changeDetection: ChangeDetectionStrategy.OnPush,changeDetection: ChangeDetectionStrategy.Default,High,https://angular.dev/best-practices/skipping-component-subtrees
28,Performance,Use trackBy in @for blocks,Stable identity for list items prevents full DOM re-creation on change,track item.id in @for,"@for (item of items; track item.id) { <li>{{ item.name }}</li> }","@for (item of items; track $index) { <li>{{ item.name }}</li> }",High,https://angular.dev/guide/templates/control-flow#track-and-identity
29,Performance,Use @defer for below-the-fold content,Defer blocks lazy-load components when they enter the viewport,@defer with on viewport for non-critical UI,Eagerly loading all components at startup,"@defer (on viewport) { <HeavyChart /> } @placeholder { <Skeleton /> }","<HeavyChart /> loaded at startup",High,https://angular.dev/guide/defer
30,Performance,Use NgOptimizedImage,Enforces image best practices: lazy loading LCP hints and proper sizing,NgOptimizedImage for all img tags,Plain img tags for CMS or user content,"<img ngSrc=""/hero.jpg"" width=""800"" height=""400"" priority />","<img src=""/hero.jpg"" />",High,https://angular.dev/guide/image-optimization
31,Performance,Tree-shake unused Angular features,Import only what you use from Angular packages,Import specific Angular modules needed,Import BrowserAnimationsModule when not using animations,"import { NgOptimizedImage } from '@angular/common'","import { CommonModule } from '@angular/common' // entire module",Medium,https://angular.dev/tools/cli/build
32,Performance,Avoid subscribe in components,Subscriptions leak and cause bugs; prefer async pipe or toSignal,toSignal() or async pipe instead of manual subscribe,Manual subscribe without unsubscribe in ngOnDestroy,"readonly data = toSignal(this.service.data$)","this.service.data$.subscribe(d => this.data = d)",High,https://angular.dev/guide/rxjs-interop
33,Performance,Use SSR with Angular Universal,Pre-render pages for faster LCP and better SEO,SSR or SSG for public-facing routes,Pure CSR for SEO-critical pages,"ng add @angular/ssr","// no SSR, client renders empty shell",Medium,https://angular.dev/guide/ssr
34,Performance,Minimize bundle with standalone APIs,Standalone components + provideRouter() eliminate dead NgModule code,provideRouter() and provideHttpClient() in app.config,Root AppModule with all imports,provideRouter(routes) in app.config.ts,"@NgModule({ imports: [RouterModule.forRoot(routes)] })",Medium,https://angular.dev/guide/routing/standalone
35,Testing,Use TestBed for component tests,TestBed sets up Angular DI for realistic component testing,TestBed.configureTestingModule for component tests,Instantiate components with new keyword,"TestBed.configureTestingModule({ imports: [MyComponent] })","const comp = new MyComponent()",High,https://angular.dev/guide/testing/components-basics
36,Testing,Use Angular CDK component harnesses,Harnesses provide a stable testing API that survives template refactors,MatButtonHarness and custom HarnessLoader,Direct native element queries that break on template changes,"const btn = await loader.getHarness(MatButtonHarness)","fixture.debugElement.query(By.css('button'))",Medium,https://material.angular.io/cdk/test-harnesses/overview
37,Testing,Use Spectator for less boilerplate,Spectator wraps TestBed with a cleaner API reducing test setup noise,Spectator for unit tests,Raw TestBed for every test,"const spectator = createComponentFactory(MyComponent)","TestBed.configureTestingModule({ declarations: [MyComponent] providers: [...] })",Low,https://github.com/ngneat/spectator
38,Testing,Mock services with jasmine.createSpyObj,Isolate unit tests by providing mock implementations of dependencies,SpyObj or jest.fn() mocks for services,Real HTTP calls in unit tests,"const spy = jasmine.createSpyObj('UserService' ['getUser']); spy.getUser.and.returnValue(of(user))","providers: [UserService] // real service in unit test",High,https://angular.dev/guide/testing/services
39,Testing,Write integration tests for routes,Test full route navigation including guards and resolvers,RouterTestingHarness for route integration tests,Mock all routing behavior in unit tests,"const harness = await RouterTestingHarness.create(); await harness.navigateByUrl('/home')","// manually calling route guard methods",Medium,https://angular.dev/api/router/testing/RouterTestingHarness
40,Testing,Test signal-based components,Signals update synchronously; no async flush needed in most cases,Read signal value directly in test assertions,TestBed.tick() or fakeAsync for signal reads,"component.count.set(5); expect(component.double()).toBe(10)","fakeAsync(() => { component.count.set(5); tick(); expect(component.double()).toBe(10) })",Medium,https://angular.dev/guide/testing
41,Styling,Use ViewEncapsulation.Emulated,Default emulation scopes styles to component preventing global leaks,Emulated or None for intentional global styles,ViewEncapsulation.None for component-specific styles,ViewEncapsulation.Emulated (default),ViewEncapsulation.None on feature components,Medium,https://angular.dev/guide/components/styling#style-scoping
42,Styling,Use :host selector,Style the component's host element using :host pseudo-class,":host for host element styles",Adding wrapper div just for styling,":host { display: block; padding: 1rem }","<div class=""wrapper"">...</div> + .wrapper { padding: 1rem }",Medium,https://angular.dev/guide/components/styling#host-element
43,Styling,Use CSS custom properties for theming,CSS variables work across component boundaries and enable dynamic theming,CSS custom properties for colors and spacing,Hardcoded hex values in component styles,":root { --primary: #6200ee } button { background: var(--primary) }","button { background: #6200ee }",Medium,https://angular.dev/guide/components/styling
44,Styling,Integrate Tailwind with Angular,Tailwind utilities work alongside Angular's ViewEncapsulation via global stylesheet,Add Tailwind in styles.css and use utility classes in templates,Custom CSS for layout that Tailwind already handles,"<div class=""flex items-center gap-4 p-6"">","<div class=""my-custom-flex""> /* .my-custom-flex { display: flex } */",Low,https://tailwindcss.com/docs/guides/angular
45,Styling,Use Angular Material theming tokens,Material 3 uses design tokens for systematic theming,M3 token-based theming for Angular Material,Overriding Angular Material CSS with deep selectors,"@include mat.button-theme($my-theme)","::ng-deep .mat-button { background: red }",Medium,https://material.angular.io/guide/theming
46,Architecture,Use injection tokens for config,Provide configuration via InjectionToken for testability and flexibility,InjectionToken for environment-specific values,Importing environment.ts directly in services,"const API_URL = new InjectionToken<string>('apiUrl'); provide: [{ provide: API_URL useValue: env.apiUrl }]","constructor(private env: Environment) { this.url = env.apiUrl }",Medium,https://angular.dev/guide/di/dependency-injection-providers#using-an-injectiontoken-object
47,Architecture,Use HTTP interceptors,Intercept requests for auth headers error handling and logging,Functional interceptors with withInterceptors(),Service-level header management in every request,"withInterceptors([authInterceptor errorInterceptor])","httpClient.get(url { headers: { Authorization: token } }) in every call",High,https://angular.dev/guide/http/interceptors
48,Architecture,Organize by feature not type,Feature-based folder structure scales better than type-based,Feature folders with collocated component service and routes,Flat folders: all-components/ all-services/,"src/features/checkout/checkout.component.ts checkout.service.ts checkout.routes.ts","src/components/checkout.component.ts src/services/checkout.service.ts",Medium,https://angular.dev/style-guide#folders-by-feature-structure
49,Architecture,Use environment configurations,Separate environment values for dev staging and prod via Angular build configs,angular.json fileReplacements for env configs,Hardcoded API URLs or feature flags in source,"fileReplacements: [{ replace: environment.ts with: environment.prod.ts }]","const API = 'https://api.example.com' // hardcoded in service",High,https://angular.dev/tools/cli/environments
50,Architecture,Prefer inject() over constructor DI,inject() function is composable and works in more contexts than constructor injection,inject() for dependency injection,Constructor parameters for new code,"readonly http = inject(HttpClient); readonly router = inject(Router)","constructor(private http: HttpClient private router: Router) {}",Medium,https://angular.dev/api/core/inject
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Components Use standalone components Angular 17+ default; no NgModule needed Standalone components for all new code NgModule-based components for new projects @Component({ standalone: true imports: [CommonModule] }) @NgModule({ declarations: [MyComp] }) High https://angular.dev/guide/components/importing
3 2 Components Use signals for state Signals are Angular's reactive primitive for fine-grained reactivity Signals for component state over class properties Mutable class properties without signals count = signal(0); increment() { this.count.update(v => v + 1) } count = 0; increment() { this.count++ } High https://angular.dev/guide/signals
4 3 Components Use @if/@for/@switch control flow Built-in control flow syntax replaces *ngIf/*ngFor directives @if and @for in templates *ngIf and *ngFor structural directives @if (isLoggedIn) { <Dashboard /> } @else { <Login /> } <div *ngIf="isLoggedIn"><Dashboard /></div> High https://angular.dev/guide/templates/control-flow
5 4 Components Use input() and output() signals Signal-based inputs/outputs replace @Input()/@Output() decorators input() and output() for component API @Input() and @Output() decorators name = input<string>(); clicked = output<void>() @Input() name: string; @Output() clicked = new EventEmitter() High https://angular.dev/guide/components/inputs
6 5 Components Use content projection ng-content for flexible component composition ng-content with select for named slots Rigid templates that can't be customized <ng-content select="[header]" /> <ng-content /> <div class="header">{{ title }}</div> Medium https://angular.dev/guide/components/content-projection
7 6 Components Keep components small Single responsibility; components should do one thing Extract sub-components when template exceeds 50 lines Monolithic components handling multiple concerns <UserAvatar /> <UserDetails /> <UserActions /> One 300-line component template Medium https://angular.dev/guide/components
8 7 Components Use OnPush change detection Reduces re-renders by only checking on input changes or signal updates OnPush for all components Default change detection strategy changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.Default High https://angular.dev/guide/components/lifecycle
9 8 Components Avoid direct DOM manipulation Use renderer or ElementRef sparingly; prefer template bindings Template bindings and Angular directives Direct document.querySelector or innerHTML [class.active]="isActive" this.el.nativeElement.classList.add('active') High https://angular.dev/guide/components/host-elements
10 9 Routing Lazy load feature routes Load route chunks on demand to reduce initial bundle loadComponent() for all feature routes Eager-loaded routes in app config { path: 'admin' loadComponent: () => import('./admin/admin.component') } { path: 'admin' component: AdminComponent } High https://angular.dev/guide/routing/lazy-loading
11 10 Routing Use route guards with functional API Protect routes with canActivate/canMatch functional guards Functional guards returning boolean or UrlTree Class-based guards with CanActivate interface canActivate: [() => inject(AuthService).isLoggedIn()] canActivate: [AuthGuard] High https://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access
12 11 Routing Use route resolvers for data Pre-fetch data before route activation using resolve ResolveFn for route data Fetching data in ngOnInit causing flash of empty state resolve: { user: () => inject(UserService).getUser() } Fetch in ngOnInit with loading state flickering Medium https://angular.dev/guide/routing/common-router-tasks#resolve
13 12 Routing Type route params with inject Use inject(ActivatedRoute) with signals or toSignal Typed route params via ActivatedRoute Untyped route.snapshot.params string access const id = toSignal(route.paramMap.pipe(map(p => p.get('id')))) const id = this.route.snapshot.params['id'] Medium https://angular.dev/api/router/ActivatedRoute
14 13 Routing Use nested routes for layouts Compose shared layouts using router-outlet nesting Nested routes with shared layout components Duplicating layout code across routes { path: 'app' component: ShellComponent children: [...] } Duplicate header/sidebar in each route component Medium https://angular.dev/guide/routing/router-tutorial-toh#child-route-configuration
15 14 Routing Configure preloading strategies Preload lazy modules in background after initial load PreloadAllModules or custom strategy No preloading causing delayed navigation provideRouter(routes withPreloading(PreloadAllModules)) provideRouter(routes) Low https://angular.dev/api/router/PreloadAllModules
16 15 State Use signals for local state Signals provide synchronous reactive state without RxJS overhead signal() for component-local reactive state BehaviorSubject for simple local state const items = signal<Item[]>([]); addItem(i: Item) { this.items.update(arr => [...arr i]) } items$ = new BehaviorSubject<Item[]>([]) High https://angular.dev/guide/signals
17 16 State Use computed() for derived state Lazily evaluated derived values that update when dependencies change computed() for values derived from other signals Duplicated state or manual sync readonly total = computed(() => this.items().reduce((s i) => s + i.price 0)) this.total = this.items.reduce(...) // called manually High https://angular.dev/guide/signals#computed-signals
18 17 State Use effect() carefully Effects run side effects when signals change; avoid overuse effect() for side effects like logging or localStorage sync effect() for deriving state (use computed instead) effect(() => localStorage.setItem('cart' JSON.stringify(this.cart()))) effect(() => { this.total.set(this.items().length) }) Medium https://angular.dev/guide/signals#effects
19 18 State Use NgRx Signal Store for complex state NgRx Signal Store is the modern lightweight state management for Angular @ngrx/signals SignalStore for feature state Full NgRx reducer/action/effect boilerplate for simple state const Store = signalStore(withState({ count: 0 }) withMethods(s => ({ increment: () => patchState(s { count: s.count() + 1 }) }))) createReducer(on(increment state => ({ ...state count: state.count + 1 }))) Medium https://ngrx.io/guide/signals
20 19 State Inject services for shared state Services with signals share state across components without a store Injectable service with signals for cross-component state Prop drilling or @Input chains for shared state @Injectable({ providedIn: 'root' }) class CartService { items = signal<Item[]>([]) } @Input() cartItems passed through 4 component levels Medium https://angular.dev/guide/di/creating-injectable-service
21 20 State Avoid mixing RxJS and signals unnecessarily Use toSignal() to bridge RxJS into signal world at the boundary toSignal() to convert observable to signal at component edge Subscribing in components and storing in signal manually readonly user = toSignal(this.userService.user$) this.userService.user$.subscribe(u => this.user.set(u)) Medium https://angular.dev/guide/rxjs-interop
22 21 Forms Use typed reactive forms FormGroup/FormControl with explicit generics for compile-time safety FormBuilder with typed controls Untyped FormControl or any casts fb.group<LoginForm>({ email: fb.control('') password: fb.control('') }) new FormGroup({ email: new FormControl(null) }) High https://angular.dev/guide/forms/typed-forms
23 22 Forms Use reactive forms over template-driven Reactive forms scale better and are fully testable ReactiveFormsModule for all non-trivial forms FormsModule with ngModel for complex forms <input [formControl]="emailControl" /> <input [(ngModel)]="email" /> Medium https://angular.dev/guide/forms/reactive-forms
24 23 Forms Write custom validators as functions Functional validators are composable and tree-shakeable ValidatorFn functions for custom validation Class-based validators implementing Validator interface const noSpaces: ValidatorFn = ctrl => ctrl.value?.includes(' ') ? { noSpaces: true } : null class NoSpacesValidator implements Validator { validate(c) {} } Medium https://angular.dev/guide/forms/form-validation#custom-validators
25 24 Forms Use updateOn for performance Control when validation runs to avoid per-keystroke validation overhead updateOn: 'blur' or 'submit' for expensive validators Default updateOn: 'change' for async validators fb.control('' { updateOn: 'blur' validators: [Validators.email] }) fb.control('' [Validators.email]) // validates on every key Low https://angular.dev/api/forms/AbstractControl#updateOn
26 25 Forms Use FormArray for dynamic fields FormArray manages variable-length lists of controls FormArray for add/remove field scenarios Manually tracking index-based controls get items(): FormArray { return this.form.get('items') as FormArray } items: [FormControl] managed outside form Medium https://angular.dev/guide/forms/reactive-forms#using-the-formarray-class
27 26 Forms Display validation errors clearly Use form control touched and dirty states to show errors at the right time Show errors after field is touched Show all errors on page load @if (email.invalid && email.touched) { <span>Invalid email</span> } @if (email.invalid) { <span>Invalid email</span> } Medium https://angular.dev/guide/forms/form-validation
28 27 Performance Apply OnPush to all components OnPush + signals eliminates most unnecessary change detection cycles OnPush change detection everywhere Default strategy which checks entire tree on every event changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.Default High https://angular.dev/best-practices/skipping-component-subtrees
29 28 Performance Use trackBy in @for blocks Stable identity for list items prevents full DOM re-creation on change track item.id in @for @for (item of items; track item.id) { <li>{{ item.name }}</li> } @for (item of items; track $index) { <li>{{ item.name }}</li> } High https://angular.dev/guide/templates/control-flow#track-and-identity
30 29 Performance Use @defer for below-the-fold content Defer blocks lazy-load components when they enter the viewport @defer with on viewport for non-critical UI Eagerly loading all components at startup @defer (on viewport) { <HeavyChart /> } @placeholder { <Skeleton /> } <HeavyChart /> loaded at startup High https://angular.dev/guide/defer
31 30 Performance Use NgOptimizedImage Enforces image best practices: lazy loading LCP hints and proper sizing NgOptimizedImage for all img tags Plain img tags for CMS or user content <img ngSrc="/hero.jpg" width="800" height="400" priority /> <img src="/hero.jpg" /> High https://angular.dev/guide/image-optimization
32 31 Performance Tree-shake unused Angular features Import only what you use from Angular packages Import specific Angular modules needed Import BrowserAnimationsModule when not using animations import { NgOptimizedImage } from '@angular/common' import { CommonModule } from '@angular/common' // entire module Medium https://angular.dev/tools/cli/build
33 32 Performance Avoid subscribe in components Subscriptions leak and cause bugs; prefer async pipe or toSignal toSignal() or async pipe instead of manual subscribe Manual subscribe without unsubscribe in ngOnDestroy readonly data = toSignal(this.service.data$) this.service.data$.subscribe(d => this.data = d) High https://angular.dev/guide/rxjs-interop
34 33 Performance Use SSR with Angular Universal Pre-render pages for faster LCP and better SEO SSR or SSG for public-facing routes Pure CSR for SEO-critical pages ng add @angular/ssr // no SSR, client renders empty shell Medium https://angular.dev/guide/ssr
35 34 Performance Minimize bundle with standalone APIs Standalone components + provideRouter() eliminate dead NgModule code provideRouter() and provideHttpClient() in app.config Root AppModule with all imports provideRouter(routes) in app.config.ts @NgModule({ imports: [RouterModule.forRoot(routes)] }) Medium https://angular.dev/guide/routing/standalone
36 35 Testing Use TestBed for component tests TestBed sets up Angular DI for realistic component testing TestBed.configureTestingModule for component tests Instantiate components with new keyword TestBed.configureTestingModule({ imports: [MyComponent] }) const comp = new MyComponent() High https://angular.dev/guide/testing/components-basics
37 36 Testing Use Angular CDK component harnesses Harnesses provide a stable testing API that survives template refactors MatButtonHarness and custom HarnessLoader Direct native element queries that break on template changes const btn = await loader.getHarness(MatButtonHarness) fixture.debugElement.query(By.css('button')) Medium https://material.angular.io/cdk/test-harnesses/overview
38 37 Testing Use Spectator for less boilerplate Spectator wraps TestBed with a cleaner API reducing test setup noise Spectator for unit tests Raw TestBed for every test const spectator = createComponentFactory(MyComponent) TestBed.configureTestingModule({ declarations: [MyComponent] providers: [...] }) Low https://github.com/ngneat/spectator
39 38 Testing Mock services with jasmine.createSpyObj Isolate unit tests by providing mock implementations of dependencies SpyObj or jest.fn() mocks for services Real HTTP calls in unit tests const spy = jasmine.createSpyObj('UserService' ['getUser']); spy.getUser.and.returnValue(of(user)) providers: [UserService] // real service in unit test High https://angular.dev/guide/testing/services
40 39 Testing Write integration tests for routes Test full route navigation including guards and resolvers RouterTestingHarness for route integration tests Mock all routing behavior in unit tests const harness = await RouterTestingHarness.create(); await harness.navigateByUrl('/home') // manually calling route guard methods Medium https://angular.dev/api/router/testing/RouterTestingHarness
41 40 Testing Test signal-based components Signals update synchronously; no async flush needed in most cases Read signal value directly in test assertions TestBed.tick() or fakeAsync for signal reads component.count.set(5); expect(component.double()).toBe(10) fakeAsync(() => { component.count.set(5); tick(); expect(component.double()).toBe(10) }) Medium https://angular.dev/guide/testing
42 41 Styling Use ViewEncapsulation.Emulated Default emulation scopes styles to component preventing global leaks Emulated or None for intentional global styles ViewEncapsulation.None for component-specific styles ViewEncapsulation.Emulated (default) ViewEncapsulation.None on feature components Medium https://angular.dev/guide/components/styling#style-scoping
43 42 Styling Use :host selector Style the component's host element using :host pseudo-class :host for host element styles Adding wrapper div just for styling :host { display: block; padding: 1rem } <div class="wrapper">...</div> + .wrapper { padding: 1rem } Medium https://angular.dev/guide/components/styling#host-element
44 43 Styling Use CSS custom properties for theming CSS variables work across component boundaries and enable dynamic theming CSS custom properties for colors and spacing Hardcoded hex values in component styles :root { --primary: #6200ee } button { background: var(--primary) } button { background: #6200ee } Medium https://angular.dev/guide/components/styling
45 44 Styling Integrate Tailwind with Angular Tailwind utilities work alongside Angular's ViewEncapsulation via global stylesheet Add Tailwind in styles.css and use utility classes in templates Custom CSS for layout that Tailwind already handles <div class="flex items-center gap-4 p-6"> <div class="my-custom-flex"> /* .my-custom-flex { display: flex } */ Low https://tailwindcss.com/docs/guides/angular
46 45 Styling Use Angular Material theming tokens Material 3 uses design tokens for systematic theming M3 token-based theming for Angular Material Overriding Angular Material CSS with deep selectors @include mat.button-theme($my-theme) ::ng-deep .mat-button { background: red } Medium https://material.angular.io/guide/theming
47 46 Architecture Use injection tokens for config Provide configuration via InjectionToken for testability and flexibility InjectionToken for environment-specific values Importing environment.ts directly in services const API_URL = new InjectionToken<string>('apiUrl'); provide: [{ provide: API_URL useValue: env.apiUrl }] constructor(private env: Environment) { this.url = env.apiUrl } Medium https://angular.dev/guide/di/dependency-injection-providers#using-an-injectiontoken-object
48 47 Architecture Use HTTP interceptors Intercept requests for auth headers error handling and logging Functional interceptors with withInterceptors() Service-level header management in every request withInterceptors([authInterceptor errorInterceptor]) httpClient.get(url { headers: { Authorization: token } }) in every call High https://angular.dev/guide/http/interceptors
49 48 Architecture Organize by feature not type Feature-based folder structure scales better than type-based Feature folders with collocated component service and routes Flat folders: all-components/ all-services/ src/features/checkout/checkout.component.ts checkout.service.ts checkout.routes.ts src/components/checkout.component.ts src/services/checkout.service.ts Medium https://angular.dev/style-guide#folders-by-feature-structure
50 49 Architecture Use environment configurations Separate environment values for dev staging and prod via Angular build configs angular.json fileReplacements for env configs Hardcoded API URLs or feature flags in source fileReplacements: [{ replace: environment.ts with: environment.prod.ts }] const API = 'https://api.example.com' // hardcoded in service High https://angular.dev/tools/cli/environments
51 50 Architecture Prefer inject() over constructor DI inject() function is composable and works in more contexts than constructor injection inject() for dependency injection Constructor parameters for new code readonly http = inject(HttpClient); readonly router = inject(Router) constructor(private http: HttpClient private router: Router) {} Medium https://angular.dev/api/core/inject

View File

@@ -0,0 +1,54 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Architecture,Use Islands Architecture,Astro's partial hydration only loads JS for interactive components,Interactive components with client directives,Hydrate entire page like traditional SPA,<Counter client:load />,Everything as client component,High,https://docs.astro.build/en/concepts/islands/
2,Architecture,Default to zero JS,Astro ships zero JS by default - add only when needed,Static components without client directive,Add client:load to everything,<Header /> (static),<Header client:load /> (unnecessary),High,https://docs.astro.build/en/basics/astro-components/
3,Architecture,Choose right client directive,Different directives for different hydration timing,client:visible for below-fold client:idle for non-critical,client:load for everything,<Comments client:visible />,<Comments client:load />,Medium,https://docs.astro.build/en/reference/directives-reference/#client-directives
4,Architecture,Use content collections,Type-safe content management for blogs docs,Content collections for structured content,Loose markdown files without schema,const posts = await getCollection('blog'),import.meta.glob('./posts/*.md'),High,https://docs.astro.build/en/guides/content-collections/
5,Architecture,Define collection schemas,Zod schemas for content validation,Schema with required fields and types,No schema validation,"defineCollection({ schema: z.object({...}) })",defineCollection({}),High,https://docs.astro.build/en/guides/content-collections/#defining-a-collection-schema
6,Routing,Use file-based routing,Create routes by adding .astro files in pages/,pages/ directory for routes,Manual route configuration,src/pages/about.astro,Custom router setup,Medium,https://docs.astro.build/en/basics/astro-pages/
7,Routing,Dynamic routes with brackets,Use [param] for dynamic routes,Bracket notation for params,Query strings for dynamic content,pages/blog/[slug].astro,pages/blog.astro?slug=x,Medium,https://docs.astro.build/en/guides/routing/#dynamic-routes
8,Routing,Use getStaticPaths for SSG,Generate static pages at build time,getStaticPaths for known dynamic routes,Fetch at runtime for static content,"export async function getStaticPaths() { return [...] }",No getStaticPaths with dynamic route,High,https://docs.astro.build/en/reference/api-reference/#getstaticpaths
9,Routing,Enable SSR when needed,Server-side rendering for dynamic content,output: 'server' or 'hybrid' for dynamic,SSR for purely static sites,"export const prerender = false;",SSR for static blog,Medium,https://docs.astro.build/en/guides/server-side-rendering/
10,Components,Keep .astro for static,Use .astro components for static content,Astro components for layout structure,React/Vue for static markup,<Layout><slot /></Layout>,<ReactLayout>{children}</ReactLayout>,High,
11,Components,Use framework components for interactivity,React Vue Svelte for complex interactivity,Framework component with client directive,Astro component with inline scripts,<ReactCounter client:load />,<script> in .astro for complex state,Medium,https://docs.astro.build/en/guides/framework-components/
12,Components,Pass data via props,Astro components receive props in frontmatter,Astro.props for component data,Global state for simple data,"const { title } = Astro.props;",Import global store,Low,https://docs.astro.build/en/basics/astro-components/#component-props
13,Components,Use slots for composition,Named and default slots for flexible layouts,<slot /> for child content,Props for HTML content,<slot name="header" />,<Component header={<div>...</div>} />,Medium,https://docs.astro.build/en/basics/astro-components/#slots
14,Components,Colocate component styles,Scoped styles in component file,<style> in same .astro file,Separate CSS files for component styles,<style> .card { } </style>,import './Card.css',Low,
15,Styling,Use scoped styles by default,Astro scopes styles to component automatically,<style> for component-specific styles,Global styles for everything,<style> h1 { } </style> (scoped),<style is:global> for everything,Medium,https://docs.astro.build/en/guides/styling/#scoped-styles
16,Styling,Use is:global sparingly,Global styles only when truly needed,is:global for base styles or overrides,is:global for component styles,<style is:global> body { } </style>,<style is:global> .card { } </style>,Medium,
17,Styling,Integrate Tailwind properly,Use @astrojs/tailwind integration,Official Tailwind integration,Manual Tailwind setup,npx astro add tailwind,Manual PostCSS config,Low,https://docs.astro.build/en/guides/integrations-guide/tailwind/
18,Styling,Use CSS variables for theming,Define tokens in :root,CSS custom properties for themes,Hardcoded colors everywhere,:root { --primary: #3b82f6; },color: #3b82f6; everywhere,Medium,
19,Data,Fetch in frontmatter,Data fetching in component frontmatter,Top-level await in frontmatter,useEffect for initial data,const data = await fetch(url),client-side fetch on mount,High,https://docs.astro.build/en/guides/data-fetching/
20,Data,Use Astro.glob for local files,Import multiple local files,Astro.glob for markdown/data files,Manual imports for each file,const posts = await Astro.glob('./posts/*.md'),"import post1; import post2;",Medium,
21,Data,Prefer content collections over glob,Type-safe collections for structured content,getCollection() for blog/docs,Astro.glob for structured content,await getCollection('blog'),await Astro.glob('./blog/*.md'),High,
22,Data,Use environment variables correctly,Import.meta.env for env vars,PUBLIC_ prefix for client vars,Expose secrets to client,import.meta.env.PUBLIC_API_URL,import.meta.env.SECRET in client,High,https://docs.astro.build/en/guides/environment-variables/
23,Performance,Preload critical assets,Use link preload for important resources,Preload fonts above-fold images,No preload hints,"<link rel=""preload"" href=""font.woff2"" as=""font"">",No preload for critical assets,Medium,
24,Performance,Optimize images with astro:assets,Built-in image optimization,<Image /> component for optimization,<img> for local images,"import { Image } from 'astro:assets';","<img src=""./image.jpg"">",High,https://docs.astro.build/en/guides/images/
25,Performance,Use picture for responsive images,Multiple formats and sizes,<Picture /> for art direction,Single image size for all screens,<Picture /> with multiple sources,<Image /> with single size,Medium,
26,Performance,Lazy load below-fold content,Defer loading non-critical content,loading=lazy for images client:visible for components,Load everything immediately,"<img loading=""lazy"">",No lazy loading,Medium,
27,Performance,Minimize client directives,Each directive adds JS bundle,Audit client: usage regularly,Sprinkle client:load everywhere,Only interactive components hydrated,Every component with client:load,High,
28,ViewTransitions,Enable View Transitions,Smooth page transitions,<ViewTransitions /> in head,Full page reloads,"import { ViewTransitions } from 'astro:transitions';",No transition API,Medium,https://docs.astro.build/en/guides/view-transitions/
29,ViewTransitions,Use transition:name,Named elements for morphing,transition:name for persistent elements,Unnamed transitions,"<header transition:name=""header"">",<header> without name,Low,
30,ViewTransitions,Handle transition:persist,Keep state across navigations,transition:persist for media players,Re-initialize on every navigation,"<video transition:persist id=""player"">",Video restarts on navigation,Medium,
31,ViewTransitions,Add fallback for no-JS,Graceful degradation,Content works without JS,Require JS for basic navigation,Static content accessible,Broken without ViewTransitions JS,High,
32,SEO,Use built-in SEO component,Head management for meta tags,Astro SEO integration or manual head,No meta tags,"<title>{title}</title><meta name=""description"">",No SEO tags,High,
33,SEO,Generate sitemap,Automatic sitemap generation,@astrojs/sitemap integration,Manual sitemap maintenance,npx astro add sitemap,Hand-written sitemap.xml,Medium,https://docs.astro.build/en/guides/integrations-guide/sitemap/
34,SEO,Add RSS feed for content,RSS for blogs and content sites,@astrojs/rss for feed generation,No RSS feed,rss() helper in pages/rss.xml.js,No feed for blog,Low,https://docs.astro.build/en/guides/rss/
35,SEO,Use canonical URLs,Prevent duplicate content issues,Astro.url for canonical generation,"<link rel=""canonical"" href={Astro.url}>",No canonical tags,Medium,
36,Integrations,Use official integrations,Astro's integration system,npx astro add for integrations,Manual configuration,npx astro add react,Manual React setup,Medium,https://docs.astro.build/en/guides/integrations-guide/
37,Integrations,Configure integrations in astro.config,Centralized configuration,integrations array in config,Scattered configuration,"integrations: [react(), tailwind()]",Multiple config files,Low,
38,Integrations,Use adapter for deployment,Platform-specific adapters,Correct adapter for host,Wrong or no adapter,@astrojs/vercel for Vercel,No adapter for SSR,High,https://docs.astro.build/en/guides/deploy/
39,TypeScript,Enable TypeScript,Type safety for Astro projects,tsconfig.json with astro types,No TypeScript,Astro TypeScript template,JavaScript only,Medium,https://docs.astro.build/en/guides/typescript/
40,TypeScript,Type component props,Define prop interfaces,Props interface in frontmatter,Untyped props,"interface Props { title: string }",No props typing,Medium,
41,TypeScript,Use strict mode,Catch errors early,strict: true in tsconfig,Loose TypeScript config,strictest template,base template,Low,
42,Markdown,Use MDX for components,Components in markdown content,@astrojs/mdx for interactive docs,Plain markdown with workarounds,<Component /> in .mdx,HTML in .md files,Medium,https://docs.astro.build/en/guides/integrations-guide/mdx/
43,Markdown,Configure markdown plugins,Extend markdown capabilities,remarkPlugins rehypePlugins in config,Manual HTML for features,remarkPlugins: [remarkToc],Manual TOC in every post,Low,
44,Markdown,Use frontmatter for metadata,Structured post metadata,Frontmatter with typed schema,Inline metadata,title date in frontmatter,# Title as first line,Medium,
45,API,Use API routes for endpoints,Server endpoints in pages/api,pages/api/[endpoint].ts for APIs,External API for simple endpoints,pages/api/posts.json.ts,Separate Express server,Medium,https://docs.astro.build/en/guides/endpoints/
46,API,Return proper responses,Use Response object,new Response() with headers,Plain objects,return new Response(JSON.stringify(data)),return data,Medium,
47,API,Handle methods correctly,Export named method handlers,export GET POST handlers,Single default export,export const GET = async () => {},export default async () => {},Low,
48,Security,Sanitize user content,Prevent XSS in dynamic content,set:html only for trusted content,set:html with user input,"<Fragment set:html={sanitized} />","<div set:html={userInput} />",High,
49,Security,Use HTTPS in production,Secure connections,HTTPS for all production sites,HTTP in production,https://example.com,http://example.com,High,
50,Security,Validate API input,Check and sanitize all input,Zod validation for API routes,Trust all input,const body = schema.parse(data),const body = await request.json(),High,
51,Build,Use hybrid rendering,Mix static and dynamic pages,output: 'hybrid' for flexibility,All SSR or all static,prerender per-page basis,Single rendering mode,Medium,https://docs.astro.build/en/guides/server-side-rendering/#hybrid-rendering
52,Build,Analyze bundle size,Monitor JS bundle impact,Build output shows bundle sizes,Ignore bundle growth,Check astro build output,No size monitoring,Medium,
53,Build,Use prefetch,Preload linked pages,prefetch integration,No prefetch for navigation,npx astro add prefetch,Manual prefetch,Low,https://docs.astro.build/en/guides/prefetch/
Can't render this file because it contains an unexpected character in line 14 and column 147.

View File

@@ -0,0 +1,57 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,XAML,Use Avalonia XAML namespace,Avalonia has its own XAML namespace not WPF,xmlns= for Avalonia-specific namespace,WPF xmlns or UWP xmlns,"<Window xmlns=""https://github.com/avaloniaui"">","<Window xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">",High,https://docs.avaloniaui.net/docs/fundamentals/avalonia-xaml
2,XAML,Use compiled bindings with x:DataType,Enable compile-time binding validation,x:DataType on root or DataTemplate for compiled bindings,Reflection-based bindings in production,"<Window x:DataType=""vm:MainViewModel""><TextBlock Text=""{Binding Name}""/></Window>","<Window><TextBlock Text=""{Binding Name}""/> without x:DataType",High,https://docs.avaloniaui.net/docs/data-binding/compiled-bindings
3,XAML,Enable compiled bindings globally,AvaloniaUseCompiledBindingsByDefault in csproj makes every binding require x:DataType and is required for trim-safe Native AOT,AvaloniaUseCompiledBindingsByDefault MSBuild property in csproj,Relying on runtime binding resolution,"<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>","No compiled binding enforcement",High,https://docs.avaloniaui.net/docs/data-binding/compiled-bindings
4,XAML,Use #name shorthand for element-to-element bindings,Compiled bindings cannot resolve {Binding ElementName=...} - use the #name shorthand which relies on NameScope lookup,#name shorthand referencing x:Name controls in the same NameScope,ElementName binding inside a compiled-binding scope,"<TextBlock Text=""{Binding #SearchBox.Text}""/>","<TextBlock Text=""{Binding ElementName=SearchBox, Path=Text}""/> under compiled bindings",Medium,https://docs.avaloniaui.net/docs/data-binding/compiled-bindings
5,Styling,Use CSS-like selectors,Avalonia uses selectors not implicit styles,Selectors targeting control types classes and pseudoclasses,WPF-style implicit Style with TargetType,"<Style Selector=""Button.primary""><Setter Property=""Background"" Value=""Blue""/></Style>","<Style TargetType=""Button""> without Selector",High,https://docs.avaloniaui.net/docs/styling/selectors
6,Styling,Use pseudoclass selectors for states,Target control states with colon syntax,:pointerover :pressed :focus for interactive states,VisualStateManager or Triggers,"<Style Selector=""Button:pointerover""><Setter Property=""Opacity"" Value=""0.8""/></Style>","<VisualStateManager> for hover effects",Medium,https://docs.avaloniaui.net/docs/styling/pseudoclasses
7,Styling,Use nesting selectors,Child and descendant combinators for scoped styles,> for direct child and space for descendant,Flat selectors that match too broadly,"<Style Selector=""StackPanel > Button""><Setter Property=""Margin"" Value=""4""/></Style>","<Style Selector=""Button""> that affects all buttons unintentionally",Medium,https://docs.avaloniaui.net/docs/styling/style-selector-syntax
8,Styling,Use StyleInclude for modularity,Split styles into separate AXAML files,StyleInclude to import themed resource files,All styles in a single monolithic App.axaml,"<StyleInclude Source=""/Styles/ButtonStyles.axaml""/>","1000+ line App.axaml with all styles",Medium,https://docs.avaloniaui.net/docs/styling/styles
9,Styling,Use Fluent or Simple theme,Built-in Avalonia themes,FluentTheme or SimpleTheme as base,Custom theme from scratch,"<FluentTheme/>","Building all control templates manually",High,https://docs.avaloniaui.net/docs/styling/themes
10,Styling,Use theme variants for dark mode,Switch between light and dark,RequestedThemeVariant for theme switching,"Hardcoded colors ignoring theme variants","Application.Current.RequestedThemeVariant = ThemeVariant.Dark;","Manually changing every brush for dark mode",Medium,https://docs.avaloniaui.net/docs/styling/themes
11,Controls,Use DataGrid for tabular data,DataGrid is a separate Avalonia.Controls.DataGrid NuGet package and requires its theme StyleInclude in App.axaml,DataGrid after adding package and StyleInclude for the matching theme,Custom Grid layouts for tabular data or DataGrid without the theme StyleInclude,"<DataGrid ItemsSource=""{Binding Items}"" AutoGenerateColumns=""False""/> with <StyleInclude Source=""avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml""/> in App.axaml","<DataGrid/> with no package reference or no StyleInclude (renders unstyled)",Medium,https://docs.avaloniaui.net/docs/reference/controls/datagrid/
12,Controls,Use TreeView with TreeDataTemplate,Avalonia uses TreeDataTemplate for hierarchical data - HierarchicalDataTemplate is WPF only,TreeDataTemplate inside TreeView.ItemTemplate with ItemsSource pointing at child collection,HierarchicalDataTemplate copied from WPF or nested ItemsControls,"<TreeView ItemsSource=""{Binding Nodes}""><TreeView.ItemTemplate><TreeDataTemplate ItemsSource=""{Binding Children}""><TextBlock Text=""{Binding Name}""/></TreeDataTemplate></TreeView.ItemTemplate></TreeView>","<TreeView><TreeView.ItemTemplate><HierarchicalDataTemplate/></TreeView.ItemTemplate></TreeView> // HierarchicalDataTemplate does not exist in Avalonia",High,https://docs.avaloniaui.net/docs/reference/controls/treeview-1
13,Controls,Use NativeMenu for platform menus,Native menu bar on macOS and desktop,NativeMenu for cross-platform menu bar,Custom menu implementation per platform,"<NativeMenu.Menu><NativeMenu><NativeMenuItem Header=""File""/></NativeMenu></NativeMenu.Menu>","Custom menu bar control for each platform",Medium,https://docs.avaloniaui.net/docs/reference/controls/nativemenu
14,Data Binding,Implement INotifyPropertyChanged,Standard .NET property notification,INotifyPropertyChanged or CommunityToolkit.Mvvm,Properties without change notification,"[ObservableProperty] private string _name;","public string Name { get; set; } without notification",High,https://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged
15,Data Binding,Use ObservableCollection for lists,UI updates on collection changes,ObservableCollection<T> for bound collections,List<T> for ItemsSources,"ObservableCollection<Item> Items { get; } = new();","List<Item> Items { get; set; }",High,https://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged
16,Data Binding,Use binding to named controls,Element-to-element binding with # syntax,#ElementName.Property for cross-element binding,Code-behind for element references,"<TextBlock Text=""{Binding #slider.Value, StringFormat='{}{0:F0}'}""/>","Code-behind ValueChanged handler to update TextBlock",Medium,https://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding
17,Data Binding,Use converters or FuncValueConverter,Transform data for display,FuncValueConverter for simple inline conversions,Complex IValueConverter classes for trivial transforms,"public static FuncValueConverter<bool, IBrush> BoolToColor = new(b => b ? Brushes.Green : Brushes.Red);","Full IValueConverter class for bool to color",Medium,https://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter
18,Cross-Platform,Use platform-specific code carefully,Isolate platform code behind abstractions,Interface + platform implementation pattern,#if directives scattered through ViewModels,"IPlatformService with platform-specific implementations","#if WINDOWS ... #elif LINUX ... in ViewModel",Medium,https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/
19,Cross-Platform,Test on all target platforms,Rendering and behavior varies across platforms,CI testing on Windows macOS and Linux,Testing only on development platform,"GitHub Actions matrix with windows-latest ubuntu-latest macos-latest","Testing only on Windows assuming cross-platform works",High,https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/
20,Cross-Platform,Handle platform file paths,Path separators differ across OS,Path.Combine and Environment.SpecialFolder,Hardcoded backslashes or forward slashes,"Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ""MyApp"")","@""C:\Users\data\config.json""",Medium,https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/dealing-with-platforms
21,Cross-Platform,Use Avalonia asset system,Platform-agnostic resource loading,avares:// URI scheme for embedded resources,File system paths for assets,"<Image Source=""avares://MyApp/Assets/logo.png""/>","<Image Source=""C:/images/logo.png""/>",High,https://docs.avaloniaui.net/docs/fundamentals/including-assets
22,Performance,Use virtualization for large lists,Only render visible items,ListBox and ItemsRepeater with virtualization,Non-virtualizing ItemsControl for large lists,"<ListBox ItemsSource=""{Binding LargeList}""/>","<ItemsControl><StackPanel> for 10K items",High,https://docs.avaloniaui.net/docs/reference/controls/listbox
23,Performance,Avoid unnecessary bindings,Each binding has overhead,Bind only properties that change,Binding static labels and headers,"<TextBlock Text=""{Binding DynamicTitle}""/> but static: <TextBlock Text=""Settings""/>","<TextBlock Text=""{Binding SettingsLabel}""/> for constant string",Low,https://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding
24,Performance,Set bitmap interpolation mode on scaled images,RenderOptions.BitmapInterpolationMode controls image scaling quality vs cost; default may look aliased on upscaled or downscaled bitmaps,RenderOptions.SetBitmapInterpolationMode tuned to the use case,Default interpolation on scaled images that look blurry or aliased,"RenderOptions.SetBitmapInterpolationMode(image, BitmapInterpolationMode.HighQuality);","Image scaled with Stretch and no interpolation hint set",Low,https://docs.avaloniaui.net/docs/concepts/image-interpolation
25,Performance,Profile with Avalonia DevTools,Built-in diagnostic tools,DevTools for visual tree and binding inspection,Console.WriteLine debugging,Attach DevTools in debug mode with F12,Print statements to debug layout issues,Medium,https://docs.avaloniaui.net/docs/guides/implementation-guides/developer-tools
26,Architecture,Use MVVM with ReactiveUI or CommunityToolkit,Proven MVVM frameworks for Avalonia,ReactiveUI or CommunityToolkit.Mvvm for ViewModels,Code-behind for all logic,"public class MainViewModel : ReactiveObject { }","MainWindow.axaml.cs with all business logic",High,https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/
27,Architecture,Use ViewLocator pattern,Convention-based View-ViewModel resolution,ViewLocator for automatic view resolution,Manual view instantiation and DataContext wiring,"class ViewLocator : IDataTemplate { Build(object data) => new MainView(); }","new MainView { DataContext = new MainViewModel() } everywhere",Medium,https://docs.avaloniaui.net/docs/data-templates/view-locator
28,Architecture,Use dependency injection,Register services in a Microsoft.Extensions.DependencyInjection container during startup before any view is constructed - resolve ViewModels through the provider not via a static ServiceLocator,Build the ServiceProvider in BuildAvaloniaApp or OnFrameworkInitializationCompleted then resolve ViewModels from it,Static ServiceLocator or new-ing ViewModels inline in code-behind,"services.AddSingleton<IDataService, DataService>(); services.AddTransient<MainViewModel>(); var provider = services.BuildServiceProvider(); // wired before windows are created","ServiceLocator.Current.GetInstance<IDataService>() called from random ViewModels with no registration ordering",Medium,https://docs.avaloniaui.net/docs/app-development/dependency-injection
29,Architecture,Separate Views from ViewModels,Keep UI and logic in separate projects,ViewModels in a separate class library,ViewModels in the same project referencing Avalonia types,"MyApp.Core (no Avalonia refs) + MyApp.Desktop (Avalonia views)","ViewModel importing Avalonia.Controls",Medium,https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/
30,Accessibility,Set AutomationProperties,Enable screen reader support,AutomationProperties.Name on interactive controls,Controls without accessible names,"<Button AutomationProperties.Name=""Close dialog""><PathIcon Data=""...""/></Button>","<Button><PathIcon/></Button> without accessible name",High,https://docs.avaloniaui.net/api/avalonia/automation/automationproperties
31,Accessibility,Support keyboard navigation,Full keyboard operability,TabIndex and KeyboardNavigation properties,Mouse-only interactions,"<Button TabIndex=""1"" Content=""Save""/>","Clickable controls without keyboard support",High,https://docs.avaloniaui.net/docs/input-interaction/keyboard-and-hotkeys
32,Accessibility,Use semantic control types,Controls convey meaning to assistive tech,Button for actions ListBox for selection,TextBlock with PointerPressed as fake button,"<Button Content=""Submit""/>","<TextBlock PointerPressed=""OnSubmitClick"" Text=""Submit""/>",High,https://docs.avaloniaui.net/docs/reference/controls/
33,Testing,Use Avalonia.Headless for UI tests,Run UI tests without display server,Avalonia.Headless for CI-compatible UI testing,Skipping UI tests in CI,"[AvaloniaTest] public void Button_Click_Updates_Label() { ... }","UI tests that require a display server",Medium,https://docs.avaloniaui.net/docs/concepts/headless/
34,Testing,Unit test ViewModels,Test business logic independently,xUnit or NUnit on ViewModel methods,Testing through UI only,"[Fact] public void AddItem_IncreasesCount() { vm.AddItem(); Assert.Equal(1, vm.Items.Count); }","Manual testing by running the app",Medium,https://docs.avaloniaui.net/docs/concepts/headless/
35,Testing,Test converters independently,Value converters contain testable logic,Unit tests on Convert and ConvertBack,Assuming converters work without tests,"[Fact] public void BoolToColor_True_ReturnsGreen() { Assert.Equal(Brushes.Green, converter.Convert(true)); }","No converter tests",Low,https://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter
36,Navigation,Use ReactiveUI routing for navigation,IScreen and RoutingState for page navigation,ReactiveUI RoutingState with IScreen on main ViewModel,Manual content swapping in code-behind,"public RoutingState Router { get; } = new(); Router.Navigate.Execute(new DetailViewModel());","contentControl.Content = new DetailView(); in code-behind",Medium,https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing
37,Navigation,Use UserControl for views,Pages and screens should be UserControls hosted in a ContentControl,UserControl for each view with RoutedViewHost or ContentControl,Window per page or nested Windows,"<UserControl x:Class=""MyApp.Views.DetailView"">","new Window() for each page in the app",Medium,https://docs.avaloniaui.net/docs/custom-controls/
38,Navigation,Use page transitions for view switching,Built-in transitions for smooth navigation,CrossFade PageSlide or CompositePageTransition declared as a property element,Abrupt content swaps with no visual continuity,"<RoutedViewHost><RoutedViewHost.PageTransition><PageSlide Orientation=""Horizontal"" Duration=""0:0:0.3""/></RoutedViewHost.PageTransition></RoutedViewHost>","ContentControl with no transition between views",Low,https://docs.avaloniaui.net/docs/reference/controls/transitioningcontentcontrol
39,Navigation,Support back navigation,Maintain navigation history for complex apps,Router.NavigateBack or custom back stack,No way to return to previous views,"<Button Command=""{Binding Router.NavigateBack}"" Content=""Back""/>","Single-direction navigation with no back support",Medium,https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing
40,Controls,Use AutoCompleteBox for search,Built-in autocomplete and suggestion control,AutoCompleteBox with FilterMode and ItemsSource,TextBox with manual Popup and ListBox for suggestions,"<AutoCompleteBox ItemsSource=""{Binding Suggestions}"" FilterMode=""Contains""/>","TextBox with custom Popup for autocomplete",Medium,https://docs.avaloniaui.net/docs/reference/controls/autocompletebox
41,Controls,Use TabControl for tabbed interfaces,Standard tabbed navigation and content switching,TabControl with TabItem for tabbed layouts,Manual toggle buttons swapping content,"<TabControl><TabItem Header=""General""><GeneralView/></TabItem><TabItem Header=""Advanced""><AdvancedView/></TabItem></TabControl>","ToggleButtons with manual content switching logic",Medium,https://docs.avaloniaui.net/docs/reference/controls/tabcontrol
42,Controls,Use SplitView for master-detail,Collapsible pane layout for navigation or panels,SplitView with Pane and Content areas,Manual Grid with column toggling for sidebar,"<SplitView IsPaneOpen=""{Binding IsPaneOpen}"" DisplayMode=""Inline""><SplitView.Pane><ListBox/></SplitView.Pane><ContentControl/></SplitView>","Grid with manual column width animation for sidebar",Medium,https://docs.avaloniaui.net/docs/reference/controls/splitview
43,Controls,Use Flyout for contextual actions,Attach popup menus and actions to controls,Flyout and MenuFlyout on Button or other controls,Custom Popup positioning and management,"<Button Content=""Options""><Button.Flyout><MenuFlyout><MenuItem Header=""Edit""/><MenuItem Header=""Delete""/></MenuFlyout></Button.Flyout></Button>","Custom Popup with manual open/close and positioning",Medium,https://docs.avaloniaui.net/docs/reference/controls/flyouts
44,Lifecycle,Use AppBuilder for app configuration,Configure platform features and services at startup,AppBuilder with UsePlatformDetect and fluent API,Manual platform initialization,"AppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().StartWithClassicDesktopLifetime(args);","Manual platform-specific startup code per OS",High,https://docs.avaloniaui.net/docs/fundamentals/application-lifetimes
45,Lifecycle,Initialize MainWindow in OnFrameworkInitializationCompleted,Override OnFrameworkInitializationCompleted on App and check ApplicationLifetime - on desktop cast to IClassicDesktopStyleApplicationLifetime to set MainWindow and ShutdownMode; never create windows in the App constructor before the framework is ready,Override OnFrameworkInitializationCompleted and pattern-match on IClassicDesktopStyleApplicationLifetime for desktop-only setup,Creating windows in the App constructor or assuming the same lifetime type on every platform,"public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow(); desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; } base.OnFrameworkInitializationCompleted(); }","public App() { new MainWindow().Show(); } // window created before framework init and ignores lifetime type",High,https://docs.avaloniaui.net/docs/fundamentals/application-lifetimes
46,Animation,Use CSS-like keyframe animations,Avalonia supports declarative animations in XAML and code,Animation with KeyFrame and Setter for property animations,Manual timer-based property updates,"<Border.Transitions><DoubleTransition Property=""Opacity"" Duration=""0:0:0.3""/></Border.Transitions>","DispatcherTimer ticking to update Opacity manually",Medium,https://docs.avaloniaui.net/docs/graphics-animation/animations
47,Animation,Use Transitions for implicit animations,Automatic animation when property values change,Transitions collection on controls for smooth changes,Instant property changes with no visual feedback,"<Button.Transitions><TransformOperationsTransition Property=""RenderTransform"" Duration=""0:0:0.2""/></Button.Transitions>","Direct property set with no transition",Low,https://docs.avaloniaui.net/docs/graphics-animation/control-transitions
48,Performance,Use compiled bindings and TrimmerRoots.xml for PublishAot,Avalonia 11+ supports Native AOT for self-contained desktop deployments; XAML reflection paths must use compiled bindings or be preserved via TrimmerRoots so trimming does not strip them,x:CompileBindings=True on every view plus TrimmerRoots.xml for runtime-resolved types,PublishAot with reflection-based {Binding} markup or trimming without checking warnings,"<UserControl x:CompileBindings=""True"" x:DataType=""vm:MainViewModel""/> with <PublishAot>true</PublishAot> and TrimmerRoots.xml listing reflected types","<PublishAot>true</PublishAot> with default <Binding> markup and no TrimmerRoots configuration",Medium,https://docs.avaloniaui.net/docs/deployment/native-aot
49,Threading,Marshal cross-thread work to the UI thread,Avalonia controls and bound properties are not thread-safe and touching them off the UI thread throws InvalidOperationException,Dispatcher.UIThread.Post or InvokeAsync to bounce work back to the UI thread,Direct property writes from Task.Run or background threads,"await Dispatcher.UIThread.InvokeAsync(() => Status = ""Done"");","Task.Run(() => { Status = ""Done""; }); // throws Call from invalid thread",High,https://docs.avaloniaui.net/docs/app-development/threading
50,Commands,Use AsyncRelayCommand or ReactiveCommand for async work,Async-aware commands disable themselves while running and surface CancellationToken so users cannot double-invoke a long operation,[RelayCommand] async Task method or ReactiveCommand.CreateFromTask,async void event handlers or fire-and-forget Task.Run from a click handler,"[RelayCommand] private async Task LoadAsync(CancellationToken ct) { await _api.GetAsync(ct); }","private async void OnClick(object s, RoutedEventArgs e) { await LongOperation(); }",High,https://docs.avaloniaui.net/docs/input-interaction/commanding
51,Styling,Use DynamicResource for theme-aware brushes,ResourceDictionary.ThemeDictionaries entries must be looked up via DynamicResource - StaticResource resolves once at load and won't update when the active theme variant changes,DynamicResource for brushes and colors that follow the active theme variant,Hardcoded hex colors or StaticResource for values that should follow theme,"<Border Background=""{DynamicResource SystemControlBackgroundAccentBrush}""/>","<Border Background=""#FF0078D4""/> while the rest of the app honors light/dark variants",Medium,https://docs.avaloniaui.net/docs/app-development/resource-dictionary
52,Input,Use HotKey or KeyBinding for keyboard shortcuts,Built-in HotKey on ICommandSource and Window.KeyBindings handle modifier keys focus scoping and cross-platform Ctrl/Cmd mapping,HotKey on a command-bound control or KeyBinding on the Window,Manual KeyDown handlers checking Key and KeyModifiers,"<Button Command=""{Binding SaveCommand}"" HotKey=""Ctrl+S"" Content=""Save""/>","OnKeyDown checking e.Key == Key.S && e.KeyModifiers == KeyModifiers.Control",Medium,https://docs.avaloniaui.net/docs/input-interaction/mouse-and-keyboard-shortcuts
53,Windowing,Customize window chrome with ExtendClientAreaToDecorationsHint,Set ExtendClientAreaToDecorationsHint to extend content into the title bar area and tag a region with WindowDecorationProperties.ElementRole=TitleBar to keep native drag and maximize behavior,ExtendClientAreaToDecorationsHint plus a region tagged ElementRole=TitleBar,SystemDecorations=None with hand-rolled PointerPressed dragging in code-behind,"<Window ExtendClientAreaToDecorationsHint=""True""><Border WindowDecorationProperties.ElementRole=""TitleBar""/></Window>","<Window SystemDecorations=""None""> with manual BeginMoveDrag from code-behind",Medium,https://docs.avaloniaui.net/docs/app-development/window-management
54,Storage,Use TopLevel.StorageProvider for file pickers,The legacy OpenFileDialog/SaveFileDialog APIs are obsolete in Avalonia 11 - use TopLevel.GetTopLevel(this).StorageProvider with OpenFilePickerAsync/SaveFilePickerAsync/OpenFolderPickerAsync which returns IStorageFile/IStorageFolder and works on desktop mobile and browser,TopLevel.StorageProvider with OpenFilePickerAsync and FilePickerOpenOptions,OpenFileDialog or SaveFileDialog from older Avalonia samples or copied from WPF,"var top = TopLevel.GetTopLevel(this); var files = await top.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = false });","var dlg = new OpenFileDialog(); var paths = await dlg.ShowAsync(window); // obsolete API",High,https://docs.avaloniaui.net/docs/concepts/services/storage-provider/
55,Windowing,Use TrayIcon for system tray icon,TrayIcon shows a native system tray/notification-area icon with a NativeMenu - declare it via the Application.TrayIcon.Icons attached property in App.axaml; works on Windows macOS and most Linux desktops,TrayIcon with NativeMenu inside TrayIcon.Icons on the Application,Custom borderless window pretending to be a tray icon or per-platform native interop,"<TrayIcon.Icons><TrayIcons><TrayIcon Icon=""/Assets/tray.ico"" ToolTipText=""MyApp""><TrayIcon.Menu><NativeMenu><NativeMenuItem Header=""Show"" Command=""{Binding ShowCommand}""/></NativeMenu></TrayIcon.Menu></TrayIcon></TrayIcons></TrayIcon.Icons>","Hidden Window with custom shell-notification-area P/Invoke",Medium,https://docs.avaloniaui.net/docs/controls/tray-icon
56,Cross-Platform,Use OnPlatform and OnFormFactor markup for per-OS values,OnPlatform and OnFormFactor markup extensions resolve to a different value per OS or form factor at XAML load time and replace if-statements in code-behind for tweaks like fonts spacing or icon sizes,OnPlatform with Default Windows macOS Linux entries directly in the property setter,RuntimeInformation.IsOSPlatform branches in code-behind to set XAML properties,"<TextBlock FontFamily=""{OnPlatform Default='Inter', Windows='Segoe UI', macOS='SF Pro Text', Linux='Ubuntu'}""/>","if (OperatingSystem.IsWindows()) textBlock.FontFamily = new(""Segoe UI""); else if (OperatingSystem.IsMacOS()) ...",Medium,https://docs.avaloniaui.net/docs/platform-specific-guides/xaml
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 XAML Use Avalonia XAML namespace Avalonia has its own XAML namespace not WPF xmlns= for Avalonia-specific namespace WPF xmlns or UWP xmlns <Window xmlns="https://github.com/avaloniaui"> <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> High https://docs.avaloniaui.net/docs/fundamentals/avalonia-xaml
3 2 XAML Use compiled bindings with x:DataType Enable compile-time binding validation x:DataType on root or DataTemplate for compiled bindings Reflection-based bindings in production <Window x:DataType="vm:MainViewModel"><TextBlock Text="{Binding Name}"/></Window> <Window><TextBlock Text="{Binding Name}"/> without x:DataType High https://docs.avaloniaui.net/docs/data-binding/compiled-bindings
4 3 XAML Enable compiled bindings globally AvaloniaUseCompiledBindingsByDefault in csproj makes every binding require x:DataType and is required for trim-safe Native AOT AvaloniaUseCompiledBindingsByDefault MSBuild property in csproj Relying on runtime binding resolution <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> No compiled binding enforcement High https://docs.avaloniaui.net/docs/data-binding/compiled-bindings
5 4 XAML Use #name shorthand for element-to-element bindings Compiled bindings cannot resolve {Binding ElementName=...} - use the #name shorthand which relies on NameScope lookup #name shorthand referencing x:Name controls in the same NameScope ElementName binding inside a compiled-binding scope <TextBlock Text="{Binding #SearchBox.Text}"/> <TextBlock Text="{Binding ElementName=SearchBox, Path=Text}"/> under compiled bindings Medium https://docs.avaloniaui.net/docs/data-binding/compiled-bindings
6 5 Styling Use CSS-like selectors Avalonia uses selectors not implicit styles Selectors targeting control types classes and pseudoclasses WPF-style implicit Style with TargetType <Style Selector="Button.primary"><Setter Property="Background" Value="Blue"/></Style> <Style TargetType="Button"> without Selector High https://docs.avaloniaui.net/docs/styling/selectors
7 6 Styling Use pseudoclass selectors for states Target control states with colon syntax :pointerover :pressed :focus for interactive states VisualStateManager or Triggers <Style Selector="Button:pointerover"><Setter Property="Opacity" Value="0.8"/></Style> <VisualStateManager> for hover effects Medium https://docs.avaloniaui.net/docs/styling/pseudoclasses
8 7 Styling Use nesting selectors Child and descendant combinators for scoped styles > for direct child and space for descendant Flat selectors that match too broadly <Style Selector="StackPanel > Button"><Setter Property="Margin" Value="4"/></Style> <Style Selector="Button"> that affects all buttons unintentionally Medium https://docs.avaloniaui.net/docs/styling/style-selector-syntax
9 8 Styling Use StyleInclude for modularity Split styles into separate AXAML files StyleInclude to import themed resource files All styles in a single monolithic App.axaml <StyleInclude Source="/Styles/ButtonStyles.axaml"/> 1000+ line App.axaml with all styles Medium https://docs.avaloniaui.net/docs/styling/styles
10 9 Styling Use Fluent or Simple theme Built-in Avalonia themes FluentTheme or SimpleTheme as base Custom theme from scratch <FluentTheme/> Building all control templates manually High https://docs.avaloniaui.net/docs/styling/themes
11 10 Styling Use theme variants for dark mode Switch between light and dark RequestedThemeVariant for theme switching Hardcoded colors ignoring theme variants Application.Current.RequestedThemeVariant = ThemeVariant.Dark; Manually changing every brush for dark mode Medium https://docs.avaloniaui.net/docs/styling/themes
12 11 Controls Use DataGrid for tabular data DataGrid is a separate Avalonia.Controls.DataGrid NuGet package and requires its theme StyleInclude in App.axaml DataGrid after adding package and StyleInclude for the matching theme Custom Grid layouts for tabular data or DataGrid without the theme StyleInclude <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False"/> with <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/> in App.axaml <DataGrid/> with no package reference or no StyleInclude (renders unstyled) Medium https://docs.avaloniaui.net/docs/reference/controls/datagrid/
13 12 Controls Use TreeView with TreeDataTemplate Avalonia uses TreeDataTemplate for hierarchical data - HierarchicalDataTemplate is WPF only TreeDataTemplate inside TreeView.ItemTemplate with ItemsSource pointing at child collection HierarchicalDataTemplate copied from WPF or nested ItemsControls <TreeView ItemsSource="{Binding Nodes}"><TreeView.ItemTemplate><TreeDataTemplate ItemsSource="{Binding Children}"><TextBlock Text="{Binding Name}"/></TreeDataTemplate></TreeView.ItemTemplate></TreeView> <TreeView><TreeView.ItemTemplate><HierarchicalDataTemplate/></TreeView.ItemTemplate></TreeView> // HierarchicalDataTemplate does not exist in Avalonia High https://docs.avaloniaui.net/docs/reference/controls/treeview-1
14 13 Controls Use NativeMenu for platform menus Native menu bar on macOS and desktop NativeMenu for cross-platform menu bar Custom menu implementation per platform <NativeMenu.Menu><NativeMenu><NativeMenuItem Header="File"/></NativeMenu></NativeMenu.Menu> Custom menu bar control for each platform Medium https://docs.avaloniaui.net/docs/reference/controls/nativemenu
15 14 Data Binding Implement INotifyPropertyChanged Standard .NET property notification INotifyPropertyChanged or CommunityToolkit.Mvvm Properties without change notification [ObservableProperty] private string _name; public string Name { get; set; } without notification High https://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged
16 15 Data Binding Use ObservableCollection for lists UI updates on collection changes ObservableCollection<T> for bound collections List<T> for ItemsSources ObservableCollection<Item> Items { get; } = new(); List<Item> Items { get; set; } High https://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged
17 16 Data Binding Use binding to named controls Element-to-element binding with # syntax #ElementName.Property for cross-element binding Code-behind for element references <TextBlock Text="{Binding #slider.Value, StringFormat='{}{0:F0}'}"/> Code-behind ValueChanged handler to update TextBlock Medium https://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding
18 17 Data Binding Use converters or FuncValueConverter Transform data for display FuncValueConverter for simple inline conversions Complex IValueConverter classes for trivial transforms public static FuncValueConverter<bool, IBrush> BoolToColor = new(b => b ? Brushes.Green : Brushes.Red); Full IValueConverter class for bool to color Medium https://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter
19 18 Cross-Platform Use platform-specific code carefully Isolate platform code behind abstractions Interface + platform implementation pattern #if directives scattered through ViewModels IPlatformService with platform-specific implementations #if WINDOWS ... #elif LINUX ... in ViewModel Medium https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/
20 19 Cross-Platform Test on all target platforms Rendering and behavior varies across platforms CI testing on Windows macOS and Linux Testing only on development platform GitHub Actions matrix with windows-latest ubuntu-latest macos-latest Testing only on Windows assuming cross-platform works High https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/
21 20 Cross-Platform Handle platform file paths Path separators differ across OS Path.Combine and Environment.SpecialFolder Hardcoded backslashes or forward slashes Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyApp") @"C:\Users\data\config.json" Medium https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/dealing-with-platforms
22 21 Cross-Platform Use Avalonia asset system Platform-agnostic resource loading avares:// URI scheme for embedded resources File system paths for assets <Image Source="avares://MyApp/Assets/logo.png"/> <Image Source="C:/images/logo.png"/> High https://docs.avaloniaui.net/docs/fundamentals/including-assets
23 22 Performance Use virtualization for large lists Only render visible items ListBox and ItemsRepeater with virtualization Non-virtualizing ItemsControl for large lists <ListBox ItemsSource="{Binding LargeList}"/> <ItemsControl><StackPanel> for 10K items High https://docs.avaloniaui.net/docs/reference/controls/listbox
24 23 Performance Avoid unnecessary bindings Each binding has overhead Bind only properties that change Binding static labels and headers <TextBlock Text="{Binding DynamicTitle}"/> but static: <TextBlock Text="Settings"/> <TextBlock Text="{Binding SettingsLabel}"/> for constant string Low https://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding
25 24 Performance Set bitmap interpolation mode on scaled images RenderOptions.BitmapInterpolationMode controls image scaling quality vs cost; default may look aliased on upscaled or downscaled bitmaps RenderOptions.SetBitmapInterpolationMode tuned to the use case Default interpolation on scaled images that look blurry or aliased RenderOptions.SetBitmapInterpolationMode(image, BitmapInterpolationMode.HighQuality); Image scaled with Stretch and no interpolation hint set Low https://docs.avaloniaui.net/docs/concepts/image-interpolation
26 25 Performance Profile with Avalonia DevTools Built-in diagnostic tools DevTools for visual tree and binding inspection Console.WriteLine debugging Attach DevTools in debug mode with F12 Print statements to debug layout issues Medium https://docs.avaloniaui.net/docs/guides/implementation-guides/developer-tools
27 26 Architecture Use MVVM with ReactiveUI or CommunityToolkit Proven MVVM frameworks for Avalonia ReactiveUI or CommunityToolkit.Mvvm for ViewModels Code-behind for all logic public class MainViewModel : ReactiveObject { } MainWindow.axaml.cs with all business logic High https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/
28 27 Architecture Use ViewLocator pattern Convention-based View-ViewModel resolution ViewLocator for automatic view resolution Manual view instantiation and DataContext wiring class ViewLocator : IDataTemplate { Build(object data) => new MainView(); } new MainView { DataContext = new MainViewModel() } everywhere Medium https://docs.avaloniaui.net/docs/data-templates/view-locator
29 28 Architecture Use dependency injection Register services in a Microsoft.Extensions.DependencyInjection container during startup before any view is constructed - resolve ViewModels through the provider not via a static ServiceLocator Build the ServiceProvider in BuildAvaloniaApp or OnFrameworkInitializationCompleted then resolve ViewModels from it Static ServiceLocator or new-ing ViewModels inline in code-behind services.AddSingleton<IDataService, DataService>(); services.AddTransient<MainViewModel>(); var provider = services.BuildServiceProvider(); // wired before windows are created ServiceLocator.Current.GetInstance<IDataService>() called from random ViewModels with no registration ordering Medium https://docs.avaloniaui.net/docs/app-development/dependency-injection
30 29 Architecture Separate Views from ViewModels Keep UI and logic in separate projects ViewModels in a separate class library ViewModels in the same project referencing Avalonia types MyApp.Core (no Avalonia refs) + MyApp.Desktop (Avalonia views) ViewModel importing Avalonia.Controls Medium https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/
31 30 Accessibility Set AutomationProperties Enable screen reader support AutomationProperties.Name on interactive controls Controls without accessible names <Button AutomationProperties.Name="Close dialog"><PathIcon Data="..."/></Button> <Button><PathIcon/></Button> without accessible name High https://docs.avaloniaui.net/api/avalonia/automation/automationproperties
32 31 Accessibility Support keyboard navigation Full keyboard operability TabIndex and KeyboardNavigation properties Mouse-only interactions <Button TabIndex="1" Content="Save"/> Clickable controls without keyboard support High https://docs.avaloniaui.net/docs/input-interaction/keyboard-and-hotkeys
33 32 Accessibility Use semantic control types Controls convey meaning to assistive tech Button for actions ListBox for selection TextBlock with PointerPressed as fake button <Button Content="Submit"/> <TextBlock PointerPressed="OnSubmitClick" Text="Submit"/> High https://docs.avaloniaui.net/docs/reference/controls/
34 33 Testing Use Avalonia.Headless for UI tests Run UI tests without display server Avalonia.Headless for CI-compatible UI testing Skipping UI tests in CI [AvaloniaTest] public void Button_Click_Updates_Label() { ... } UI tests that require a display server Medium https://docs.avaloniaui.net/docs/concepts/headless/
35 34 Testing Unit test ViewModels Test business logic independently xUnit or NUnit on ViewModel methods Testing through UI only [Fact] public void AddItem_IncreasesCount() { vm.AddItem(); Assert.Equal(1, vm.Items.Count); } Manual testing by running the app Medium https://docs.avaloniaui.net/docs/concepts/headless/
36 35 Testing Test converters independently Value converters contain testable logic Unit tests on Convert and ConvertBack Assuming converters work without tests [Fact] public void BoolToColor_True_ReturnsGreen() { Assert.Equal(Brushes.Green, converter.Convert(true)); } No converter tests Low https://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter
37 36 Navigation Use ReactiveUI routing for navigation IScreen and RoutingState for page navigation ReactiveUI RoutingState with IScreen on main ViewModel Manual content swapping in code-behind public RoutingState Router { get; } = new(); Router.Navigate.Execute(new DetailViewModel()); contentControl.Content = new DetailView(); in code-behind Medium https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing
38 37 Navigation Use UserControl for views Pages and screens should be UserControls hosted in a ContentControl UserControl for each view with RoutedViewHost or ContentControl Window per page or nested Windows <UserControl x:Class="MyApp.Views.DetailView"> new Window() for each page in the app Medium https://docs.avaloniaui.net/docs/custom-controls/
39 38 Navigation Use page transitions for view switching Built-in transitions for smooth navigation CrossFade PageSlide or CompositePageTransition declared as a property element Abrupt content swaps with no visual continuity <RoutedViewHost><RoutedViewHost.PageTransition><PageSlide Orientation="Horizontal" Duration="0:0:0.3"/></RoutedViewHost.PageTransition></RoutedViewHost> ContentControl with no transition between views Low https://docs.avaloniaui.net/docs/reference/controls/transitioningcontentcontrol
40 39 Navigation Support back navigation Maintain navigation history for complex apps Router.NavigateBack or custom back stack No way to return to previous views <Button Command="{Binding Router.NavigateBack}" Content="Back"/> Single-direction navigation with no back support Medium https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing
41 40 Controls Use AutoCompleteBox for search Built-in autocomplete and suggestion control AutoCompleteBox with FilterMode and ItemsSource TextBox with manual Popup and ListBox for suggestions <AutoCompleteBox ItemsSource="{Binding Suggestions}" FilterMode="Contains"/> TextBox with custom Popup for autocomplete Medium https://docs.avaloniaui.net/docs/reference/controls/autocompletebox
42 41 Controls Use TabControl for tabbed interfaces Standard tabbed navigation and content switching TabControl with TabItem for tabbed layouts Manual toggle buttons swapping content <TabControl><TabItem Header="General"><GeneralView/></TabItem><TabItem Header="Advanced"><AdvancedView/></TabItem></TabControl> ToggleButtons with manual content switching logic Medium https://docs.avaloniaui.net/docs/reference/controls/tabcontrol
43 42 Controls Use SplitView for master-detail Collapsible pane layout for navigation or panels SplitView with Pane and Content areas Manual Grid with column toggling for sidebar <SplitView IsPaneOpen="{Binding IsPaneOpen}" DisplayMode="Inline"><SplitView.Pane><ListBox/></SplitView.Pane><ContentControl/></SplitView> Grid with manual column width animation for sidebar Medium https://docs.avaloniaui.net/docs/reference/controls/splitview
44 43 Controls Use Flyout for contextual actions Attach popup menus and actions to controls Flyout and MenuFlyout on Button or other controls Custom Popup positioning and management <Button Content="Options"><Button.Flyout><MenuFlyout><MenuItem Header="Edit"/><MenuItem Header="Delete"/></MenuFlyout></Button.Flyout></Button> Custom Popup with manual open/close and positioning Medium https://docs.avaloniaui.net/docs/reference/controls/flyouts
45 44 Lifecycle Use AppBuilder for app configuration Configure platform features and services at startup AppBuilder with UsePlatformDetect and fluent API Manual platform initialization AppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().StartWithClassicDesktopLifetime(args); Manual platform-specific startup code per OS High https://docs.avaloniaui.net/docs/fundamentals/application-lifetimes
46 45 Lifecycle Initialize MainWindow in OnFrameworkInitializationCompleted Override OnFrameworkInitializationCompleted on App and check ApplicationLifetime - on desktop cast to IClassicDesktopStyleApplicationLifetime to set MainWindow and ShutdownMode; never create windows in the App constructor before the framework is ready Override OnFrameworkInitializationCompleted and pattern-match on IClassicDesktopStyleApplicationLifetime for desktop-only setup Creating windows in the App constructor or assuming the same lifetime type on every platform public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow(); desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; } base.OnFrameworkInitializationCompleted(); } public App() { new MainWindow().Show(); } // window created before framework init and ignores lifetime type High https://docs.avaloniaui.net/docs/fundamentals/application-lifetimes
47 46 Animation Use CSS-like keyframe animations Avalonia supports declarative animations in XAML and code Animation with KeyFrame and Setter for property animations Manual timer-based property updates <Border.Transitions><DoubleTransition Property="Opacity" Duration="0:0:0.3"/></Border.Transitions> DispatcherTimer ticking to update Opacity manually Medium https://docs.avaloniaui.net/docs/graphics-animation/animations
48 47 Animation Use Transitions for implicit animations Automatic animation when property values change Transitions collection on controls for smooth changes Instant property changes with no visual feedback <Button.Transitions><TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2"/></Button.Transitions> Direct property set with no transition Low https://docs.avaloniaui.net/docs/graphics-animation/control-transitions
49 48 Performance Use compiled bindings and TrimmerRoots.xml for PublishAot Avalonia 11+ supports Native AOT for self-contained desktop deployments; XAML reflection paths must use compiled bindings or be preserved via TrimmerRoots so trimming does not strip them x:CompileBindings=True on every view plus TrimmerRoots.xml for runtime-resolved types PublishAot with reflection-based {Binding} markup or trimming without checking warnings <UserControl x:CompileBindings="True" x:DataType="vm:MainViewModel"/> with <PublishAot>true</PublishAot> and TrimmerRoots.xml listing reflected types <PublishAot>true</PublishAot> with default <Binding> markup and no TrimmerRoots configuration Medium https://docs.avaloniaui.net/docs/deployment/native-aot
50 49 Threading Marshal cross-thread work to the UI thread Avalonia controls and bound properties are not thread-safe and touching them off the UI thread throws InvalidOperationException Dispatcher.UIThread.Post or InvokeAsync to bounce work back to the UI thread Direct property writes from Task.Run or background threads await Dispatcher.UIThread.InvokeAsync(() => Status = "Done"); Task.Run(() => { Status = "Done"; }); // throws Call from invalid thread High https://docs.avaloniaui.net/docs/app-development/threading
51 50 Commands Use AsyncRelayCommand or ReactiveCommand for async work Async-aware commands disable themselves while running and surface CancellationToken so users cannot double-invoke a long operation [RelayCommand] async Task method or ReactiveCommand.CreateFromTask async void event handlers or fire-and-forget Task.Run from a click handler [RelayCommand] private async Task LoadAsync(CancellationToken ct) { await _api.GetAsync(ct); } private async void OnClick(object s, RoutedEventArgs e) { await LongOperation(); } High https://docs.avaloniaui.net/docs/input-interaction/commanding
52 51 Styling Use DynamicResource for theme-aware brushes ResourceDictionary.ThemeDictionaries entries must be looked up via DynamicResource - StaticResource resolves once at load and won't update when the active theme variant changes DynamicResource for brushes and colors that follow the active theme variant Hardcoded hex colors or StaticResource for values that should follow theme <Border Background="{DynamicResource SystemControlBackgroundAccentBrush}"/> <Border Background="#FF0078D4"/> while the rest of the app honors light/dark variants Medium https://docs.avaloniaui.net/docs/app-development/resource-dictionary
53 52 Input Use HotKey or KeyBinding for keyboard shortcuts Built-in HotKey on ICommandSource and Window.KeyBindings handle modifier keys focus scoping and cross-platform Ctrl/Cmd mapping HotKey on a command-bound control or KeyBinding on the Window Manual KeyDown handlers checking Key and KeyModifiers <Button Command="{Binding SaveCommand}" HotKey="Ctrl+S" Content="Save"/> OnKeyDown checking e.Key == Key.S && e.KeyModifiers == KeyModifiers.Control Medium https://docs.avaloniaui.net/docs/input-interaction/mouse-and-keyboard-shortcuts
54 53 Windowing Customize window chrome with ExtendClientAreaToDecorationsHint Set ExtendClientAreaToDecorationsHint to extend content into the title bar area and tag a region with WindowDecorationProperties.ElementRole=TitleBar to keep native drag and maximize behavior ExtendClientAreaToDecorationsHint plus a region tagged ElementRole=TitleBar SystemDecorations=None with hand-rolled PointerPressed dragging in code-behind <Window ExtendClientAreaToDecorationsHint="True"><Border WindowDecorationProperties.ElementRole="TitleBar"/></Window> <Window SystemDecorations="None"> with manual BeginMoveDrag from code-behind Medium https://docs.avaloniaui.net/docs/app-development/window-management
55 54 Storage Use TopLevel.StorageProvider for file pickers The legacy OpenFileDialog/SaveFileDialog APIs are obsolete in Avalonia 11 - use TopLevel.GetTopLevel(this).StorageProvider with OpenFilePickerAsync/SaveFilePickerAsync/OpenFolderPickerAsync which returns IStorageFile/IStorageFolder and works on desktop mobile and browser TopLevel.StorageProvider with OpenFilePickerAsync and FilePickerOpenOptions OpenFileDialog or SaveFileDialog from older Avalonia samples or copied from WPF var top = TopLevel.GetTopLevel(this); var files = await top.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = false }); var dlg = new OpenFileDialog(); var paths = await dlg.ShowAsync(window); // obsolete API High https://docs.avaloniaui.net/docs/concepts/services/storage-provider/
56 55 Windowing Use TrayIcon for system tray icon TrayIcon shows a native system tray/notification-area icon with a NativeMenu - declare it via the Application.TrayIcon.Icons attached property in App.axaml; works on Windows macOS and most Linux desktops TrayIcon with NativeMenu inside TrayIcon.Icons on the Application Custom borderless window pretending to be a tray icon or per-platform native interop <TrayIcon.Icons><TrayIcons><TrayIcon Icon="/Assets/tray.ico" ToolTipText="MyApp"><TrayIcon.Menu><NativeMenu><NativeMenuItem Header="Show" Command="{Binding ShowCommand}"/></NativeMenu></TrayIcon.Menu></TrayIcon></TrayIcons></TrayIcon.Icons> Hidden Window with custom shell-notification-area P/Invoke Medium https://docs.avaloniaui.net/docs/controls/tray-icon
57 56 Cross-Platform Use OnPlatform and OnFormFactor markup for per-OS values OnPlatform and OnFormFactor markup extensions resolve to a different value per OS or form factor at XAML load time and replace if-statements in code-behind for tweaks like fonts spacing or icon sizes OnPlatform with Default Windows macOS Linux entries directly in the property setter RuntimeInformation.IsOSPlatform branches in code-behind to set XAML properties <TextBlock FontFamily="{OnPlatform Default='Inter', Windows='Segoe UI', macOS='SF Pro Text', Linux='Ubuntu'}"/> if (OperatingSystem.IsWindows()) textBlock.FontFamily = new("Segoe UI"); else if (OperatingSystem.IsMacOS()) ... Medium https://docs.avaloniaui.net/docs/platform-specific-guides/xaml

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Widgets,Use StatelessWidget when possible,Immutable widgets are simpler,StatelessWidget for static UI,StatefulWidget for everything,class MyWidget extends StatelessWidget,class MyWidget extends StatefulWidget (static),Medium,https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
2,Widgets,Keep widgets small,Single responsibility principle,Extract widgets into smaller pieces,Large build methods,Column(children: [Header() Content()]),500+ line build method,Medium,
3,Widgets,Use const constructors,Compile-time constants for performance,const MyWidget() when possible,Non-const for static widgets,const Text('Hello'),Text('Hello') for literals,High,https://dart.dev/guides/language/language-tour#constant-constructors
4,Widgets,Prefer composition over inheritance,Combine widgets using children,Compose widgets,Extend widget classes,Container(child: MyContent()),class MyContainer extends Container,Medium,
5,State,Use setState correctly,Minimal state in StatefulWidget,setState for UI state changes,setState for business logic,setState(() { _counter++; }),Complex logic in setState,Medium,https://api.flutter.dev/flutter/widgets/State/setState.html
6,State,Avoid setState in build,Never call setState during build,setState in callbacks only,setState in build method,onPressed: () => setState(() {}),build() { setState(); },High,
7,State,Use state management for complex apps,Provider Riverpod BLoC,State management for shared state,setState for global state,Provider.of<MyState>(context),Global setState calls,Medium,
8,State,Prefer Riverpod or Provider,Recommended state solutions,Riverpod for new projects,InheritedWidget manually,ref.watch(myProvider),Custom InheritedWidget,Medium,https://riverpod.dev/
9,State,Dispose resources,Clean up controllers and subscriptions,dispose() for cleanup,Memory leaks from subscriptions,@override void dispose() { controller.dispose(); },No dispose implementation,High,
10,Layout,Use Column and Row,Basic layout widgets,Column Row for linear layouts,Stack for simple layouts,"Column(children: [Text(), Button()])",Stack for vertical list,Medium,https://api.flutter.dev/flutter/widgets/Column-class.html
11,Layout,Use Expanded and Flexible,Control flex behavior,Expanded to fill space,Fixed sizes in flex containers,Expanded(child: Container()),Container(width: 200) in Row,Medium,
12,Layout,Use SizedBox for spacing,Consistent spacing,SizedBox for gaps,Container for spacing only,SizedBox(height: 16),Container(height: 16),Low,
13,Layout,Use LayoutBuilder for responsive,Respond to constraints,LayoutBuilder for adaptive layouts,Fixed sizes for responsive,LayoutBuilder(builder: (context constraints) {}),Container(width: 375),Medium,https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
14,Layout,Avoid deep nesting,Keep widget tree shallow,Extract deeply nested widgets,10+ levels of nesting,Extract widget to method or class,Column(Row(Column(Row(...)))),Medium,
15,Lists,Use ListView.builder,Lazy list building,ListView.builder for long lists,ListView with children for large lists,"ListView.builder(itemCount: 100, itemBuilder: ...)",ListView(children: items.map(...).toList()),High,https://api.flutter.dev/flutter/widgets/ListView-class.html
16,Lists,Provide itemExtent when known,Skip measurement,itemExtent for fixed height items,No itemExtent for uniform lists,ListView.builder(itemExtent: 50),ListView.builder without itemExtent,Medium,
17,Lists,Use keys for stateful items,Preserve widget state,Key for stateful list items,No key for dynamic lists,ListTile(key: ValueKey(item.id)),ListTile without key,High,
18,Lists,Use SliverList for custom scroll,Custom scroll effects,CustomScrollView with Slivers,Nested ListViews,CustomScrollView(slivers: [SliverList()]),ListView inside ListView,Medium,https://api.flutter.dev/flutter/widgets/SliverList-class.html
19,Navigation,Use Navigator 2.0 or GoRouter,Declarative routing,go_router for navigation,Navigator.push for complex apps,GoRouter(routes: [...]),Navigator.push everywhere,Medium,https://pub.dev/packages/go_router
20,Navigation,Use named routes,Organized navigation,Named routes for clarity,Anonymous routes,Navigator.pushNamed(context '/home'),Navigator.push(context MaterialPageRoute()),Low,
21,Navigation,Handle back button (PopScope),Android back behavior and predictive back (Android 14+),Use PopScope widget (WillPopScope is deprecated),Use WillPopScope,"PopScope(canPop: false, onPopInvoked: (didPop) => ...)",WillPopScope(onWillPop: ...),High,https://api.flutter.dev/flutter/widgets/PopScope-class.html
22,Navigation,Pass typed arguments,Type-safe route arguments,Typed route arguments,Dynamic arguments,MyRoute(id: '123'),arguments: {'id': '123'},Medium,
23,Async,Use FutureBuilder,Async UI building,FutureBuilder for async data,setState for async,FutureBuilder(future: fetchData()),fetchData().then((d) => setState()),Medium,https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
24,Async,Use StreamBuilder,Stream UI building,StreamBuilder for streams,Manual stream subscription,StreamBuilder(stream: myStream),stream.listen in initState,Medium,https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
25,Async,Handle loading and error states,Complete async UI states,ConnectionState checks,Only success state,if (snapshot.connectionState == ConnectionState.waiting),No loading indicator,High,
26,Async,Cancel subscriptions,Clean up stream subscriptions,Cancel in dispose,Memory leaks,subscription.cancel() in dispose,No subscription cleanup,High,
27,Theming,Use ThemeData,Consistent theming,ThemeData for app theme,Hardcoded colors,Theme.of(context).primaryColor,Color(0xFF123456) everywhere,Medium,https://api.flutter.dev/flutter/material/ThemeData-class.html
28,Theming,Use ColorScheme,Material 3 color system,ColorScheme for colors,Individual color properties,colorScheme: ColorScheme.fromSeed(),primaryColor: Colors.blue,Medium,
29,Theming,Access theme via context,Dynamic theme access,Theme.of(context),Static theme reference,Theme.of(context).textTheme.bodyLarge,TextStyle(fontSize: 16),Medium,
30,Theming,Support dark mode,Respect system theme,darkTheme in MaterialApp,Light theme only,"MaterialApp(theme: light, darkTheme: dark)",MaterialApp(theme: light),Medium,
31,Animation,Use implicit animations,Simple animations,AnimatedContainer AnimatedOpacity,Explicit for simple transitions,AnimatedContainer(duration: Duration()),AnimationController for fade,Low,https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html
32,Animation,Use AnimationController for complex,Fine-grained control,AnimationController with Ticker,Implicit for complex sequences,AnimationController(vsync: this),AnimatedContainer for staggered,Medium,
33,Animation,Dispose AnimationControllers,Clean up animation resources,dispose() for controllers,Memory leaks,controller.dispose() in dispose,No controller disposal,High,
34,Animation,Use Hero for transitions,Shared element transitions,Hero for navigation animations,Manual shared element,Hero(tag: 'image' child: Image()),Custom shared element animation,Low,https://api.flutter.dev/flutter/widgets/Hero-class.html
35,Forms,Use Form widget,Form validation,Form with GlobalKey,Individual validation,Form(key: _formKey child: ...),TextField without Form,Medium,https://api.flutter.dev/flutter/widgets/Form-class.html
36,Forms,Use TextEditingController,Control text input,Controller for text fields,onChanged for all text,final controller = TextEditingController(),onChanged: (v) => setState(),Medium,
37,Forms,Validate on submit,Form validation flow,_formKey.currentState!.validate(),Skip validation,if (_formKey.currentState!.validate()),Submit without validation,High,
38,Forms,Dispose controllers,Clean up text controllers,dispose() for controllers,Memory leaks,controller.dispose() in dispose,No controller disposal,High,
39,Performance,Use const widgets,Reduce rebuilds,const for static widgets,No const for literals,const Icon(Icons.add),Icon(Icons.add),High,
40,Performance,Avoid rebuilding entire tree,Minimal rebuild scope,Isolate changing widgets,setState on parent,Consumer only around changing widget,setState on root widget,High,
41,Performance,Use RepaintBoundary,Isolate repaints,RepaintBoundary for animations,Full screen repaints,RepaintBoundary(child: AnimatedWidget()),Animation without boundary,Medium,https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html
42,Performance,Profile with DevTools,Measure before optimizing,Flutter DevTools profiling,Guess at performance,DevTools performance tab,Optimize without measuring,Medium,https://docs.flutter.dev/tools/devtools
43,Accessibility,Use Semantics widget,Screen reader support,Semantics for accessibility,Missing accessibility info,Semantics(label: 'Submit button'),GestureDetector without semantics,High,https://api.flutter.dev/flutter/widgets/Semantics-class.html
44,Accessibility,Support large fonts,MediaQuery text scaling,MediaQuery.textScaleFactor,Fixed font sizes,style: Theme.of(context).textTheme,TextStyle(fontSize: 14),High,
45,Accessibility,Test with screen readers,TalkBack and VoiceOver,Test accessibility regularly,Skip accessibility testing,Regular TalkBack testing,No screen reader testing,High,
46,Testing,Use widget tests,Test widget behavior,WidgetTester for UI tests,Unit tests only,testWidgets('...' (tester) async {}),Only test() for UI,Medium,https://docs.flutter.dev/testing
47,Testing,Use integration tests,Full app testing,integration_test package,Manual testing only,IntegrationTestWidgetsFlutterBinding,Manual E2E testing,Medium,
48,Testing,Mock dependencies,Isolate tests,Mockito or mocktail,Real dependencies in tests,when(mock.method()).thenReturn(),Real API calls in tests,Medium,
49,Platform,Use Platform checks,Platform-specific code,Platform.isIOS Platform.isAndroid,Same code for all platforms,if (Platform.isIOS) {},Hardcoded iOS behavior,Medium,
50,Platform,Use kIsWeb for web,Web platform detection,kIsWeb for web checks,Platform for web,if (kIsWeb) {},Platform.isWeb (doesn't exist),Medium,
51,Packages,Use pub.dev packages,Community packages,Popular maintained packages,Custom implementations,cached_network_image,Custom image cache,Medium,https://pub.dev/
52,Packages,Check package quality,Quality before adding,Pub points and popularity,Any package without review,100+ pub points,Unmaintained packages,Medium,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Widgets Use StatelessWidget when possible Immutable widgets are simpler StatelessWidget for static UI StatefulWidget for everything class MyWidget extends StatelessWidget class MyWidget extends StatefulWidget (static) Medium https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
3 2 Widgets Keep widgets small Single responsibility principle Extract widgets into smaller pieces Large build methods Column(children: [Header() Content()]) 500+ line build method Medium
4 3 Widgets Use const constructors Compile-time constants for performance const MyWidget() when possible Non-const for static widgets const Text('Hello') Text('Hello') for literals High https://dart.dev/guides/language/language-tour#constant-constructors
5 4 Widgets Prefer composition over inheritance Combine widgets using children Compose widgets Extend widget classes Container(child: MyContent()) class MyContainer extends Container Medium
6 5 State Use setState correctly Minimal state in StatefulWidget setState for UI state changes setState for business logic setState(() { _counter++; }) Complex logic in setState Medium https://api.flutter.dev/flutter/widgets/State/setState.html
7 6 State Avoid setState in build Never call setState during build setState in callbacks only setState in build method onPressed: () => setState(() {}) build() { setState(); } High
8 7 State Use state management for complex apps Provider Riverpod BLoC State management for shared state setState for global state Provider.of<MyState>(context) Global setState calls Medium
9 8 State Prefer Riverpod or Provider Recommended state solutions Riverpod for new projects InheritedWidget manually ref.watch(myProvider) Custom InheritedWidget Medium https://riverpod.dev/
10 9 State Dispose resources Clean up controllers and subscriptions dispose() for cleanup Memory leaks from subscriptions @override void dispose() { controller.dispose(); } No dispose implementation High
11 10 Layout Use Column and Row Basic layout widgets Column Row for linear layouts Stack for simple layouts Column(children: [Text(), Button()]) Stack for vertical list Medium https://api.flutter.dev/flutter/widgets/Column-class.html
12 11 Layout Use Expanded and Flexible Control flex behavior Expanded to fill space Fixed sizes in flex containers Expanded(child: Container()) Container(width: 200) in Row Medium
13 12 Layout Use SizedBox for spacing Consistent spacing SizedBox for gaps Container for spacing only SizedBox(height: 16) Container(height: 16) Low
14 13 Layout Use LayoutBuilder for responsive Respond to constraints LayoutBuilder for adaptive layouts Fixed sizes for responsive LayoutBuilder(builder: (context constraints) {}) Container(width: 375) Medium https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
15 14 Layout Avoid deep nesting Keep widget tree shallow Extract deeply nested widgets 10+ levels of nesting Extract widget to method or class Column(Row(Column(Row(...)))) Medium
16 15 Lists Use ListView.builder Lazy list building ListView.builder for long lists ListView with children for large lists ListView.builder(itemCount: 100, itemBuilder: ...) ListView(children: items.map(...).toList()) High https://api.flutter.dev/flutter/widgets/ListView-class.html
17 16 Lists Provide itemExtent when known Skip measurement itemExtent for fixed height items No itemExtent for uniform lists ListView.builder(itemExtent: 50) ListView.builder without itemExtent Medium
18 17 Lists Use keys for stateful items Preserve widget state Key for stateful list items No key for dynamic lists ListTile(key: ValueKey(item.id)) ListTile without key High
19 18 Lists Use SliverList for custom scroll Custom scroll effects CustomScrollView with Slivers Nested ListViews CustomScrollView(slivers: [SliverList()]) ListView inside ListView Medium https://api.flutter.dev/flutter/widgets/SliverList-class.html
20 19 Navigation Use Navigator 2.0 or GoRouter Declarative routing go_router for navigation Navigator.push for complex apps GoRouter(routes: [...]) Navigator.push everywhere Medium https://pub.dev/packages/go_router
21 20 Navigation Use named routes Organized navigation Named routes for clarity Anonymous routes Navigator.pushNamed(context '/home') Navigator.push(context MaterialPageRoute()) Low
22 21 Navigation Handle back button (PopScope) Android back behavior and predictive back (Android 14+) Use PopScope widget (WillPopScope is deprecated) Use WillPopScope PopScope(canPop: false, onPopInvoked: (didPop) => ...) WillPopScope(onWillPop: ...) High https://api.flutter.dev/flutter/widgets/PopScope-class.html
23 22 Navigation Pass typed arguments Type-safe route arguments Typed route arguments Dynamic arguments MyRoute(id: '123') arguments: {'id': '123'} Medium
24 23 Async Use FutureBuilder Async UI building FutureBuilder for async data setState for async FutureBuilder(future: fetchData()) fetchData().then((d) => setState()) Medium https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
25 24 Async Use StreamBuilder Stream UI building StreamBuilder for streams Manual stream subscription StreamBuilder(stream: myStream) stream.listen in initState Medium https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
26 25 Async Handle loading and error states Complete async UI states ConnectionState checks Only success state if (snapshot.connectionState == ConnectionState.waiting) No loading indicator High
27 26 Async Cancel subscriptions Clean up stream subscriptions Cancel in dispose Memory leaks subscription.cancel() in dispose No subscription cleanup High
28 27 Theming Use ThemeData Consistent theming ThemeData for app theme Hardcoded colors Theme.of(context).primaryColor Color(0xFF123456) everywhere Medium https://api.flutter.dev/flutter/material/ThemeData-class.html
29 28 Theming Use ColorScheme Material 3 color system ColorScheme for colors Individual color properties colorScheme: ColorScheme.fromSeed() primaryColor: Colors.blue Medium
30 29 Theming Access theme via context Dynamic theme access Theme.of(context) Static theme reference Theme.of(context).textTheme.bodyLarge TextStyle(fontSize: 16) Medium
31 30 Theming Support dark mode Respect system theme darkTheme in MaterialApp Light theme only MaterialApp(theme: light, darkTheme: dark) MaterialApp(theme: light) Medium
32 31 Animation Use implicit animations Simple animations AnimatedContainer AnimatedOpacity Explicit for simple transitions AnimatedContainer(duration: Duration()) AnimationController for fade Low https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html
33 32 Animation Use AnimationController for complex Fine-grained control AnimationController with Ticker Implicit for complex sequences AnimationController(vsync: this) AnimatedContainer for staggered Medium
34 33 Animation Dispose AnimationControllers Clean up animation resources dispose() for controllers Memory leaks controller.dispose() in dispose No controller disposal High
35 34 Animation Use Hero for transitions Shared element transitions Hero for navigation animations Manual shared element Hero(tag: 'image' child: Image()) Custom shared element animation Low https://api.flutter.dev/flutter/widgets/Hero-class.html
36 35 Forms Use Form widget Form validation Form with GlobalKey Individual validation Form(key: _formKey child: ...) TextField without Form Medium https://api.flutter.dev/flutter/widgets/Form-class.html
37 36 Forms Use TextEditingController Control text input Controller for text fields onChanged for all text final controller = TextEditingController() onChanged: (v) => setState() Medium
38 37 Forms Validate on submit Form validation flow _formKey.currentState!.validate() Skip validation if (_formKey.currentState!.validate()) Submit without validation High
39 38 Forms Dispose controllers Clean up text controllers dispose() for controllers Memory leaks controller.dispose() in dispose No controller disposal High
40 39 Performance Use const widgets Reduce rebuilds const for static widgets No const for literals const Icon(Icons.add) Icon(Icons.add) High
41 40 Performance Avoid rebuilding entire tree Minimal rebuild scope Isolate changing widgets setState on parent Consumer only around changing widget setState on root widget High
42 41 Performance Use RepaintBoundary Isolate repaints RepaintBoundary for animations Full screen repaints RepaintBoundary(child: AnimatedWidget()) Animation without boundary Medium https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html
43 42 Performance Profile with DevTools Measure before optimizing Flutter DevTools profiling Guess at performance DevTools performance tab Optimize without measuring Medium https://docs.flutter.dev/tools/devtools
44 43 Accessibility Use Semantics widget Screen reader support Semantics for accessibility Missing accessibility info Semantics(label: 'Submit button') GestureDetector without semantics High https://api.flutter.dev/flutter/widgets/Semantics-class.html
45 44 Accessibility Support large fonts MediaQuery text scaling MediaQuery.textScaleFactor Fixed font sizes style: Theme.of(context).textTheme TextStyle(fontSize: 14) High
46 45 Accessibility Test with screen readers TalkBack and VoiceOver Test accessibility regularly Skip accessibility testing Regular TalkBack testing No screen reader testing High
47 46 Testing Use widget tests Test widget behavior WidgetTester for UI tests Unit tests only testWidgets('...' (tester) async {}) Only test() for UI Medium https://docs.flutter.dev/testing
48 47 Testing Use integration tests Full app testing integration_test package Manual testing only IntegrationTestWidgetsFlutterBinding Manual E2E testing Medium
49 48 Testing Mock dependencies Isolate tests Mockito or mocktail Real dependencies in tests when(mock.method()).thenReturn() Real API calls in tests Medium
50 49 Platform Use Platform checks Platform-specific code Platform.isIOS Platform.isAndroid Same code for all platforms if (Platform.isIOS) {} Hardcoded iOS behavior Medium
51 50 Platform Use kIsWeb for web Web platform detection kIsWeb for web checks Platform for web if (kIsWeb) {} Platform.isWeb (doesn't exist) Medium
52 51 Packages Use pub.dev packages Community packages Popular maintained packages Custom implementations cached_network_image Custom image cache Medium https://pub.dev/
53 52 Packages Check package quality Quality before adding Pub points and popularity Any package without review 100+ pub points Unmaintained packages Medium

View File

@@ -0,0 +1,56 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Animation,Use Tailwind animate utilities,Built-in animations are optimized and respect reduced-motion,Use animate-pulse animate-spin animate-ping,Custom @keyframes for simple effects,animate-pulse,@keyframes pulse {...},Medium,https://tailwindcss.com/docs/animation
2,Animation,Limit bounce animations,Continuous bounce is distracting and causes motion sickness,Use animate-bounce sparingly on CTAs only,Multiple bounce animations on page,Single CTA with animate-bounce,5+ elements with animate-bounce,High,
3,Animation,Transition duration,Use appropriate transition speeds for UI feedback,duration-150 to duration-300 for UI,duration-1000 or longer for UI elements,transition-all duration-200,transition-all duration-1000,Medium,https://tailwindcss.com/docs/transition-duration
4,Animation,Hover transitions,Add smooth transitions on hover state changes,Add transition class with hover states,Instant hover changes without transition,hover:bg-gray-100 transition-colors,hover:bg-gray-100 (no transition),Low,
5,Z-Index,Use Tailwind z-* scale,Consistent stacking context with predefined scale,z-0 z-10 z-20 z-30 z-40 z-50,Arbitrary z-index values,z-50 for modals,z-[9999],Medium,https://tailwindcss.com/docs/z-index
6,Z-Index,Fixed elements z-index,Fixed navigation and modals need explicit z-index,z-50 for nav z-40 for dropdowns,Relying on DOM order for stacking,fixed top-0 z-50,fixed top-0 (no z-index),High,
7,Z-Index,Negative z-index for backgrounds,Use negative z-index for decorative backgrounds,z-[-1] for background elements,Positive z-index for backgrounds,-z-10 for decorative,z-10 for background,Low,
8,Layout,Container max-width,Limit content width for readability,max-w-7xl mx-auto for main content,Full-width content on large screens,max-w-7xl mx-auto px-4,w-full (no max-width),Medium,https://tailwindcss.com/docs/container
9,Layout,Responsive padding,Adjust padding for different screen sizes,px-4 md:px-6 lg:px-8,Same padding all sizes,px-4 sm:px-6 lg:px-8,px-8 (same all sizes),Medium,
10,Layout,Grid gaps,Use consistent gap utilities for spacing,gap-4 gap-6 gap-8,Margins on individual items,grid gap-6,grid with mb-4 on each item,Medium,https://tailwindcss.com/docs/gap
11,Layout,Flexbox alignment,Use flex utilities for alignment,items-center justify-between,Multiple nested wrappers,flex items-center justify-between,Nested divs for alignment,Low,
12,Images,Aspect ratio,Maintain consistent image aspect ratios,aspect-video aspect-square,No aspect ratio on containers,aspect-video rounded-lg,No aspect control,Medium,https://tailwindcss.com/docs/aspect-ratio
13,Images,Object fit,Control image scaling within containers,object-cover object-contain,Stretched distorted images,object-cover w-full h-full,No object-fit,Medium,https://tailwindcss.com/docs/object-fit
14,Images,Lazy loading,Defer loading of off-screen images,loading='lazy' on images,All images eager load,<img loading='lazy'>,<img> without lazy,High,
15,Images,Responsive images,Serve appropriate image sizes,srcset and sizes attributes,Same large image all devices,srcset with multiple sizes,4000px image everywhere,High,
16,Typography,Prose plugin,Use @tailwindcss/typography for rich text,prose prose-lg for article content,Custom styles for markdown,prose prose-lg max-w-none,Custom text styling,Medium,https://tailwindcss.com/docs/typography-plugin
17,Typography,Line height,Use appropriate line height for readability,leading-relaxed for body text,Default tight line height,leading-relaxed (1.625),leading-none or leading-tight,Medium,https://tailwindcss.com/docs/line-height
18,Typography,Font size scale,Use consistent text size scale,text-sm text-base text-lg text-xl,Arbitrary font sizes,text-lg,text-[17px],Low,https://tailwindcss.com/docs/font-size
19,Typography,Text truncation,Handle long text gracefully,truncate or line-clamp-*,Overflow breaking layout,line-clamp-2,No overflow handling,Medium,https://tailwindcss.com/docs/text-overflow
20,Colors,Opacity utilities,Use color opacity utilities,bg-black/50 text-white/80,Separate opacity class,bg-black/50,bg-black opacity-50,Low,https://tailwindcss.com/docs/background-color
21,Colors,Dark mode,Support dark mode with dark: prefix,dark:bg-gray-900 dark:text-white,No dark mode support,dark:bg-gray-900,Only light theme,Medium,https://tailwindcss.com/docs/dark-mode
22,Colors,Semantic colors,Use semantic color naming in config,primary secondary danger success,Generic color names in components,bg-primary,bg-blue-500 everywhere,Medium,
23,Spacing,Consistent spacing scale,Use Tailwind spacing scale consistently,p-4 m-6 gap-8,Arbitrary pixel values,p-4 (1rem),p-[15px],Low,https://tailwindcss.com/docs/customizing-spacing
24,Spacing,Negative margins,Use sparingly for overlapping effects,-mt-4 for overlapping elements,Negative margins for layout fixing,-mt-8 for card overlap,-m-2 to fix spacing issues,Medium,
25,Spacing,Space between,Use space-y-* for vertical lists,space-y-4 on flex/grid column,Margin on each child,space-y-4,Each child has mb-4,Low,https://tailwindcss.com/docs/space
26,Forms,Focus states,Always show focus indicators,focus:ring-2 focus:ring-blue-500,Remove focus outline,focus:ring-2 focus:ring-offset-2,focus:outline-none (no replacement),High,
27,Forms,Input sizing,Consistent input dimensions,h-10 px-3 for inputs,Inconsistent input heights,h-10 w-full px-3,Various heights per input,Medium,
28,Forms,Disabled states,Clear disabled styling,disabled:opacity-50 disabled:cursor-not-allowed,No disabled indication,disabled:opacity-50,Same style as enabled,Medium,
29,Forms,Placeholder styling,Style placeholder text appropriately,placeholder:text-gray-400,Dark placeholder text,placeholder:text-gray-400,Default dark placeholder,Low,
30,Responsive,Mobile-first approach,Start with mobile styles and add breakpoints,Default mobile + md: lg: xl:,Desktop-first approach,text-sm md:text-base,text-base max-md:text-sm,Medium,https://tailwindcss.com/docs/responsive-design
31,Responsive,Breakpoint testing,Test at standard breakpoints,320 375 768 1024 1280 1536,Only test on development device,Test all breakpoints,Single device testing,High,
32,Responsive,Hidden/shown utilities,Control visibility per breakpoint,hidden md:block,Different content per breakpoint,hidden md:flex,Separate mobile/desktop components,Low,https://tailwindcss.com/docs/display
33,Buttons,Button sizing,Consistent button dimensions,px-4 py-2 or px-6 py-3,Inconsistent button sizes,px-4 py-2 text-sm,Various padding per button,Medium,
34,Buttons,Touch targets,Minimum 44px touch target on mobile,min-h-[44px] on mobile,Small buttons on mobile,min-h-[44px] min-w-[44px],h-8 w-8 on mobile,High,
35,Buttons,Loading states,Show loading feedback,disabled + spinner icon,Clickable during loading,<Button disabled><Spinner/></Button>,Button without loading state,High,
36,Buttons,Icon buttons,Accessible icon-only buttons,aria-label on icon buttons,Icon button without label,<button aria-label='Close'><XIcon/></button>,<button><XIcon/></button>,High,
37,Cards,Card structure,Consistent card styling,rounded-lg shadow-md p-6,Inconsistent card styles,rounded-2xl shadow-lg p-6,Mixed card styling,Low,
38,Cards,Card hover states,Interactive cards should have hover feedback,hover:shadow-lg transition-shadow,No hover on clickable cards,hover:shadow-xl transition-shadow,Static cards that are clickable,Medium,
39,Cards,Card spacing,Consistent internal card spacing,space-y-4 for card content,Inconsistent internal spacing,space-y-4 or p-6,Mixed mb-2 mb-4 mb-6,Low,
40,Accessibility,Screen reader text,Provide context for screen readers,sr-only for hidden labels,Missing context for icons,<span class='sr-only'>Close menu</span>,No label for icon button,High,https://tailwindcss.com/docs/screen-readers
41,Accessibility,Focus visible,Show focus only for keyboard users,focus-visible:ring-2,Focus on all interactions,focus-visible:ring-2,focus:ring-2 (shows on click too),Medium,
42,Accessibility,Reduced motion,Respect user motion preferences,motion-reduce:animate-none,Ignore motion preferences,motion-reduce:transition-none,No reduced motion support,High,https://tailwindcss.com/docs/hover-focus-and-other-states#prefers-reduced-motion
43,Performance,Configure content paths,Tailwind needs to know where classes are used,Use 'content' array in config,Use deprecated 'purge' option (v2),"content: ['./src/**/*.{js,ts,jsx,tsx}']",purge: [...],High,https://tailwindcss.com/docs/content-configuration
44,Performance,JIT mode,Use JIT for faster builds and smaller bundles,JIT enabled (default in v3),Full CSS in development,Tailwind v3 defaults,Tailwind v2 without JIT,Medium,
45,Performance,Avoid @apply bloat,Use @apply sparingly,Direct utilities in HTML,Heavy @apply usage,class='px-4 py-2 rounded',@apply px-4 py-2 rounded;,Low,https://tailwindcss.com/docs/reusing-styles
46,Plugins,Official plugins,Use official Tailwind plugins,@tailwindcss/forms typography aspect-ratio,Custom implementations,@tailwindcss/forms,Custom form reset CSS,Medium,https://tailwindcss.com/docs/plugins
47,Plugins,Custom utilities,Create utilities for repeated patterns,Custom utility in config,Repeated arbitrary values,Custom shadow utility,"shadow-[0_4px_20px_rgba(0,0,0,0.1)] everywhere",Medium,
48,Layout,Container Queries,Use @container for component-based responsiveness,Use @container and @lg: etc.,Media queries for component internals,@container @lg:grid-cols-2,@media (min-width: ...) inside component,Medium,https://github.com/tailwindlabs/tailwindcss-container-queries
49,Interactivity,Group and Peer,Style based on parent/sibling state,group-hover peer-checked,JS for simple state interactions,group-hover:text-blue-500,onMouseEnter={() => setHover(true)},Low,https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
50,Customization,Arbitrary Values,Use [] for one-off values,w-[350px] for specific needs,Creating config for single use,top-[117px] (if strictly needed),style={{ top: '117px' }},Low,https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values
51,Colors,Theme color variables,Define colors in Tailwind theme and use directly,bg-primary text-success border-cta,bg-[var(--color-primary)] text-[var(--color-success)],bg-primary,bg-[var(--color-primary)],Medium,https://tailwindcss.com/docs/customizing-colors
52,Colors,Use bg-linear-to-* for gradients,Tailwind v4 uses bg-linear-to-* syntax for gradients,bg-linear-to-r bg-linear-to-b,bg-gradient-to-* (deprecated in v4),bg-linear-to-r from-blue-500 to-purple-500,bg-gradient-to-r from-blue-500 to-purple-500,Medium,https://tailwindcss.com/docs/background-image
53,Layout,Use shrink-0 shorthand,Shorter class name for flex-shrink-0,shrink-0 shrink,flex-shrink-0 flex-shrink,shrink-0,flex-shrink-0,Low,https://tailwindcss.com/docs/flex-shrink
54,Layout,Use size-* for square dimensions,Single utility for equal width and height,size-4 size-8 size-12,Separate h-* w-* for squares,size-6,h-6 w-6,Low,https://tailwindcss.com/docs/size
55,Images,SVG explicit dimensions,Add width/height attributes to SVGs to prevent layout shift before CSS loads,<svg class='size-6' width='24' height='24'>,SVG without explicit dimensions,<svg class='size-6' width='24' height='24'>,<svg class='size-6'>,High,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Animation Use Tailwind animate utilities Built-in animations are optimized and respect reduced-motion Use animate-pulse animate-spin animate-ping Custom @keyframes for simple effects animate-pulse @keyframes pulse {...} Medium https://tailwindcss.com/docs/animation
3 2 Animation Limit bounce animations Continuous bounce is distracting and causes motion sickness Use animate-bounce sparingly on CTAs only Multiple bounce animations on page Single CTA with animate-bounce 5+ elements with animate-bounce High
4 3 Animation Transition duration Use appropriate transition speeds for UI feedback duration-150 to duration-300 for UI duration-1000 or longer for UI elements transition-all duration-200 transition-all duration-1000 Medium https://tailwindcss.com/docs/transition-duration
5 4 Animation Hover transitions Add smooth transitions on hover state changes Add transition class with hover states Instant hover changes without transition hover:bg-gray-100 transition-colors hover:bg-gray-100 (no transition) Low
6 5 Z-Index Use Tailwind z-* scale Consistent stacking context with predefined scale z-0 z-10 z-20 z-30 z-40 z-50 Arbitrary z-index values z-50 for modals z-[9999] Medium https://tailwindcss.com/docs/z-index
7 6 Z-Index Fixed elements z-index Fixed navigation and modals need explicit z-index z-50 for nav z-40 for dropdowns Relying on DOM order for stacking fixed top-0 z-50 fixed top-0 (no z-index) High
8 7 Z-Index Negative z-index for backgrounds Use negative z-index for decorative backgrounds z-[-1] for background elements Positive z-index for backgrounds -z-10 for decorative z-10 for background Low
9 8 Layout Container max-width Limit content width for readability max-w-7xl mx-auto for main content Full-width content on large screens max-w-7xl mx-auto px-4 w-full (no max-width) Medium https://tailwindcss.com/docs/container
10 9 Layout Responsive padding Adjust padding for different screen sizes px-4 md:px-6 lg:px-8 Same padding all sizes px-4 sm:px-6 lg:px-8 px-8 (same all sizes) Medium
11 10 Layout Grid gaps Use consistent gap utilities for spacing gap-4 gap-6 gap-8 Margins on individual items grid gap-6 grid with mb-4 on each item Medium https://tailwindcss.com/docs/gap
12 11 Layout Flexbox alignment Use flex utilities for alignment items-center justify-between Multiple nested wrappers flex items-center justify-between Nested divs for alignment Low
13 12 Images Aspect ratio Maintain consistent image aspect ratios aspect-video aspect-square No aspect ratio on containers aspect-video rounded-lg No aspect control Medium https://tailwindcss.com/docs/aspect-ratio
14 13 Images Object fit Control image scaling within containers object-cover object-contain Stretched distorted images object-cover w-full h-full No object-fit Medium https://tailwindcss.com/docs/object-fit
15 14 Images Lazy loading Defer loading of off-screen images loading='lazy' on images All images eager load <img loading='lazy'> <img> without lazy High
16 15 Images Responsive images Serve appropriate image sizes srcset and sizes attributes Same large image all devices srcset with multiple sizes 4000px image everywhere High
17 16 Typography Prose plugin Use @tailwindcss/typography for rich text prose prose-lg for article content Custom styles for markdown prose prose-lg max-w-none Custom text styling Medium https://tailwindcss.com/docs/typography-plugin
18 17 Typography Line height Use appropriate line height for readability leading-relaxed for body text Default tight line height leading-relaxed (1.625) leading-none or leading-tight Medium https://tailwindcss.com/docs/line-height
19 18 Typography Font size scale Use consistent text size scale text-sm text-base text-lg text-xl Arbitrary font sizes text-lg text-[17px] Low https://tailwindcss.com/docs/font-size
20 19 Typography Text truncation Handle long text gracefully truncate or line-clamp-* Overflow breaking layout line-clamp-2 No overflow handling Medium https://tailwindcss.com/docs/text-overflow
21 20 Colors Opacity utilities Use color opacity utilities bg-black/50 text-white/80 Separate opacity class bg-black/50 bg-black opacity-50 Low https://tailwindcss.com/docs/background-color
22 21 Colors Dark mode Support dark mode with dark: prefix dark:bg-gray-900 dark:text-white No dark mode support dark:bg-gray-900 Only light theme Medium https://tailwindcss.com/docs/dark-mode
23 22 Colors Semantic colors Use semantic color naming in config primary secondary danger success Generic color names in components bg-primary bg-blue-500 everywhere Medium
24 23 Spacing Consistent spacing scale Use Tailwind spacing scale consistently p-4 m-6 gap-8 Arbitrary pixel values p-4 (1rem) p-[15px] Low https://tailwindcss.com/docs/customizing-spacing
25 24 Spacing Negative margins Use sparingly for overlapping effects -mt-4 for overlapping elements Negative margins for layout fixing -mt-8 for card overlap -m-2 to fix spacing issues Medium
26 25 Spacing Space between Use space-y-* for vertical lists space-y-4 on flex/grid column Margin on each child space-y-4 Each child has mb-4 Low https://tailwindcss.com/docs/space
27 26 Forms Focus states Always show focus indicators focus:ring-2 focus:ring-blue-500 Remove focus outline focus:ring-2 focus:ring-offset-2 focus:outline-none (no replacement) High
28 27 Forms Input sizing Consistent input dimensions h-10 px-3 for inputs Inconsistent input heights h-10 w-full px-3 Various heights per input Medium
29 28 Forms Disabled states Clear disabled styling disabled:opacity-50 disabled:cursor-not-allowed No disabled indication disabled:opacity-50 Same style as enabled Medium
30 29 Forms Placeholder styling Style placeholder text appropriately placeholder:text-gray-400 Dark placeholder text placeholder:text-gray-400 Default dark placeholder Low
31 30 Responsive Mobile-first approach Start with mobile styles and add breakpoints Default mobile + md: lg: xl: Desktop-first approach text-sm md:text-base text-base max-md:text-sm Medium https://tailwindcss.com/docs/responsive-design
32 31 Responsive Breakpoint testing Test at standard breakpoints 320 375 768 1024 1280 1536 Only test on development device Test all breakpoints Single device testing High
33 32 Responsive Hidden/shown utilities Control visibility per breakpoint hidden md:block Different content per breakpoint hidden md:flex Separate mobile/desktop components Low https://tailwindcss.com/docs/display
34 33 Buttons Button sizing Consistent button dimensions px-4 py-2 or px-6 py-3 Inconsistent button sizes px-4 py-2 text-sm Various padding per button Medium
35 34 Buttons Touch targets Minimum 44px touch target on mobile min-h-[44px] on mobile Small buttons on mobile min-h-[44px] min-w-[44px] h-8 w-8 on mobile High
36 35 Buttons Loading states Show loading feedback disabled + spinner icon Clickable during loading <Button disabled><Spinner/></Button> Button without loading state High
37 36 Buttons Icon buttons Accessible icon-only buttons aria-label on icon buttons Icon button without label <button aria-label='Close'><XIcon/></button> <button><XIcon/></button> High
38 37 Cards Card structure Consistent card styling rounded-lg shadow-md p-6 Inconsistent card styles rounded-2xl shadow-lg p-6 Mixed card styling Low
39 38 Cards Card hover states Interactive cards should have hover feedback hover:shadow-lg transition-shadow No hover on clickable cards hover:shadow-xl transition-shadow Static cards that are clickable Medium
40 39 Cards Card spacing Consistent internal card spacing space-y-4 for card content Inconsistent internal spacing space-y-4 or p-6 Mixed mb-2 mb-4 mb-6 Low
41 40 Accessibility Screen reader text Provide context for screen readers sr-only for hidden labels Missing context for icons <span class='sr-only'>Close menu</span> No label for icon button High https://tailwindcss.com/docs/screen-readers
42 41 Accessibility Focus visible Show focus only for keyboard users focus-visible:ring-2 Focus on all interactions focus-visible:ring-2 focus:ring-2 (shows on click too) Medium
43 42 Accessibility Reduced motion Respect user motion preferences motion-reduce:animate-none Ignore motion preferences motion-reduce:transition-none No reduced motion support High https://tailwindcss.com/docs/hover-focus-and-other-states#prefers-reduced-motion
44 43 Performance Configure content paths Tailwind needs to know where classes are used Use 'content' array in config Use deprecated 'purge' option (v2) content: ['./src/**/*.{js,ts,jsx,tsx}'] purge: [...] High https://tailwindcss.com/docs/content-configuration
45 44 Performance JIT mode Use JIT for faster builds and smaller bundles JIT enabled (default in v3) Full CSS in development Tailwind v3 defaults Tailwind v2 without JIT Medium
46 45 Performance Avoid @apply bloat Use @apply sparingly Direct utilities in HTML Heavy @apply usage class='px-4 py-2 rounded' @apply px-4 py-2 rounded; Low https://tailwindcss.com/docs/reusing-styles
47 46 Plugins Official plugins Use official Tailwind plugins @tailwindcss/forms typography aspect-ratio Custom implementations @tailwindcss/forms Custom form reset CSS Medium https://tailwindcss.com/docs/plugins
48 47 Plugins Custom utilities Create utilities for repeated patterns Custom utility in config Repeated arbitrary values Custom shadow utility shadow-[0_4px_20px_rgba(0,0,0,0.1)] everywhere Medium
49 48 Layout Container Queries Use @container for component-based responsiveness Use @container and @lg: etc. Media queries for component internals @container @lg:grid-cols-2 @media (min-width: ...) inside component Medium https://github.com/tailwindlabs/tailwindcss-container-queries
50 49 Interactivity Group and Peer Style based on parent/sibling state group-hover peer-checked JS for simple state interactions group-hover:text-blue-500 onMouseEnter={() => setHover(true)} Low https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
51 50 Customization Arbitrary Values Use [] for one-off values w-[350px] for specific needs Creating config for single use top-[117px] (if strictly needed) style={{ top: '117px' }} Low https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values
52 51 Colors Theme color variables Define colors in Tailwind theme and use directly bg-primary text-success border-cta bg-[var(--color-primary)] text-[var(--color-success)] bg-primary bg-[var(--color-primary)] Medium https://tailwindcss.com/docs/customizing-colors
53 52 Colors Use bg-linear-to-* for gradients Tailwind v4 uses bg-linear-to-* syntax for gradients bg-linear-to-r bg-linear-to-b bg-gradient-to-* (deprecated in v4) bg-linear-to-r from-blue-500 to-purple-500 bg-gradient-to-r from-blue-500 to-purple-500 Medium https://tailwindcss.com/docs/background-image
54 53 Layout Use shrink-0 shorthand Shorter class name for flex-shrink-0 shrink-0 shrink flex-shrink-0 flex-shrink shrink-0 flex-shrink-0 Low https://tailwindcss.com/docs/flex-shrink
55 54 Layout Use size-* for square dimensions Single utility for equal width and height size-4 size-8 size-12 Separate h-* w-* for squares size-6 h-6 w-6 Low https://tailwindcss.com/docs/size
56 55 Images SVG explicit dimensions Add width/height attributes to SVGs to prevent layout shift before CSS loads <svg class='size-6' width='24' height='24'> SVG without explicit dimensions <svg class='size-6' width='24' height='24'> <svg class='size-6'> High

View File

@@ -0,0 +1,76 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Application,Start UI from Application subclass,JavaFX apps should bootstrap the primary Stage through Application.start(),Extend Application and configure Scene in start(),Create UI from a random main method without launching JavaFX,"public class App extends Application { public void start(Stage stage) { stage.setScene(new Scene(root)); stage.show(); } }",new Stage().show(),High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/application/Application.html
2,Threading,Keep work off the FX Application Thread,Long-running work blocks rendering and input when executed on the UI thread,Use Task or Service for background work,Run network database or file work in button handlers,"Task<List<Item>> task = new Task<>() { protected List<Item> call() { return repo.load(); } }; new Thread(task).start();",loadLargeFile(); table.setItems(items);,High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/concurrent/Task.html
3,Threading,Update UI only on FX thread,Scene graph changes must happen on the JavaFX Application Thread,Use bindings task handlers or Platform.runLater for UI changes,Mutate controls directly from background threads,"task.setOnSucceeded(e -> table.setItems(FXCollections.observableArrayList(task.getValue())));",new Thread(() -> label.setText("Done")).start(),High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/application/Platform.html
4,Threading,Bind progress to background tasks,Task exposes progress and message properties for responsive feedback,Bind ProgressBar and Label to task properties,Poll progress manually or leave users without feedback,"progress.progressProperty().bind(task.progressProperty()); status.textProperty().bind(task.messageProperty());",while(running) progress.setProgress(x);,Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/concurrent/Task.html
5,FXML,Use FXML for stable declarative layouts,FXML keeps view structure readable for screens with many controls,Place layout in FXML and behavior in controller,Build large screens entirely in one Java method,"<VBox spacing=""12"" xmlns:fx=""http://javafx.com/fxml"" fx:controller=""app.MainController"">",VBox root = new VBox(); root.getChildren().add(... 200 lines ...);,Medium,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html
6,FXML,Keep controllers focused on view behavior,Controllers should coordinate controls and delegate business logic to services,Inject services or call application services from controller,Put database queries and domain rules directly in controller,"public void save() { customerService.save(form.toCommand()); }",public void save() { DriverManager.getConnection(...); },High,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXML.html
7,FXML,Use fx:id for injected controls,FXML controls need stable fx:id values that match controller fields,Annotate fields with @FXML and keep ids descriptive,Look up controls by CSS selector for normal wiring,"@FXML private TableView<Customer> customerTable;",root.lookup("#customerTable"),Medium,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXML.html
8,FXML,Fail fast when loading FXML,FXML load errors should surface during screen creation with clear context,Load resources with getResource and handle IOException explicitly,Swallow loader errors and show a blank scene,"URL view = getClass().getResource(\"/views/main.fxml\"); Parent root = FXMLLoader.load(view);",try { FXMLLoader.load(url); } catch(Exception ignored) {},High,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html
9,CSS,Style with style classes,JavaFX CSS works best through reusable styleClass names,Add semantic style classes and define them in CSS,Set long inline style strings throughout code,"button.getStyleClass().add(\"primary-action\");",".setStyle(\"-fx-background-color: #2563eb; -fx-padding: 12; ...\")",Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/doc-files/cssref.html
10,CSS,Use design tokens through looked-up colors,Looked-up colors keep palettes consistent across controls,Define named colors on root and reuse them in CSS,Repeat hex values in every selector,".root { -brand-primary: #2563eb; } .button.primary { -fx-background-color: -brand-primary; }",".save { -fx-background-color: #2563eb; } .link { -fx-text-fill: #2563eb; }",Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/doc-files/cssref.html
11,CSS,Avoid overusing inline effects,Expensive CSS effects and shadows can hurt desktop UI responsiveness,Use subtle shadows only on important elevated surfaces,Apply blur drop shadow and glow to every node,".dialog-card { -fx-effect: dropshadow(gaussian, rgba(0,0,0,.18), 16, 0, 0, 4); }",".table-row-cell { -fx-effect: dropshadow(gaussian, black, 20, .5, 0, 0); }",Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/effect/package-summary.html
12,Layout,Choose layout panes by responsibility,Each pane solves a different layout problem and should be selected intentionally,Use BorderPane for app shell GridPane for forms VBox/HBox for simple stacks,Use absolute positioning for resizable app screens,"BorderPane shell = new BorderPane(); shell.setTop(toolbar); shell.setCenter(content);",Pane root = new Pane(); button.setLayoutX(742);,High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/package-summary.html
13,Layout,Prefer constraints over fixed coordinates,Responsive JavaFX layouts depend on constraints and grow priorities,Use hgrow vgrow column constraints and alignment,Hard-code pixel positions and sizes,"GridPane.setHgrow(nameField, Priority.ALWAYS); column.setPercentWidth(50);",field.setPrefWidth(328); field.setLayoutX(120);,High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/GridPane.html
14,Layout,Set sensible min pref and max sizes,Controls should resize predictably across windows and DPI settings,Use Region.USE_COMPUTED_SIZE and max widths intentionally,Lock every control to fixed width and height,"button.setMaxWidth(Double.MAX_VALUE); VBox.setVgrow(table, Priority.ALWAYS);","button.setMinSize(96, 32); button.setMaxSize(96, 32);",Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/Region.html
15,Layout,Use spacing and padding consistently,Desktop UI needs scan-friendly rhythm and clear grouping,Set spacing padding and Insets through shared constants or CSS,Use inconsistent ad hoc gaps between controls,"form.setHgap(12); form.setVgap(10); form.setPadding(new Insets(16));",box.setSpacing(3); other.setSpacing(17);,Low,https://openjfx.io/javadoc/21/javafx.graphics/javafx/geometry/Insets.html
16,Controls,Use ObservableList for list controls,TableView ListView and ComboBox update automatically from observable collections,Back controls with FXCollections.observableArrayList(),Mutate plain lists and manually refresh controls,"ObservableList<Customer> rows = FXCollections.observableArrayList(); table.setItems(rows);",List<Customer> rows = new ArrayList<>(); table.setItems((ObservableList) rows);,High,https://openjfx.io/javadoc/21/javafx.base/javafx/collections/ObservableList.html
17,Controls,Configure TableView cell value factories with properties,Table columns should observe stable JavaFX properties for updates,Expose StringProperty ObjectProperty or use ReadOnlyObjectWrapper,Return transient strings without observable support,"nameCol.setCellValueFactory(data -> data.getValue().nameProperty());",nameCol.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().toString()));,Medium,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TableColumn.html
18,Controls,Use cell factories for custom rendering,Custom table or list visuals belong in reusable cell factories,Override updateItem and handle empty state,Place complex Nodes directly in model objects,"col.setCellFactory(c -> new TableCell<>() { protected void updateItem(Status s, boolean empty) { super.updateItem(s, empty); setText(empty ? null : s.label()); } });",row.setBadge(new Label("Active"));,Medium,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/Cell.html
19,Controls,Virtualized controls are for large data,TableView ListView TreeView virtualize cells and outperform manual node lists,Use TableView or ListView for hundreds of rows,Create hundreds of HBoxes inside a VBox,"ListView<Item> list = new ListView<>(items);",items.forEach(i -> vbox.getChildren().add(new ItemRow(i)));,High,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/ListView.html
20,Controls,Handle empty states explicitly,Empty tables and lists need visible guidance or next actions,Set placeholder nodes for empty data views,Leave blank white areas that look broken,"table.setPlaceholder(new Label(\"No customers match this filter\"));",table.setPlaceholder(null);,Low,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TableView.html
21,Binding,Use property binding for derived UI state,JavaFX binding reduces imperative synchronization bugs,Bind disabled visible text and progress properties to source state,Manually update every dependent control in each event handler,"saveButton.disableProperty().bind(form.validProperty().not());",if(!valid) saveButton.setDisable(true);,High,https://openjfx.io/javadoc/21/javafx.base/javafx/beans/binding/Bindings.html
22,Binding,Unbind before manual updates,Bound properties cannot be set directly without errors,Call unbind when switching from bound to manual state,Set a bound property directly,"label.textProperty().unbind(); label.setText(\"Ready\");",label.textProperty().bind(task.messageProperty()); label.setText(\"Ready\");,Medium,https://openjfx.io/javadoc/21/javafx.base/javafx/beans/property/Property.html
23,Binding,Use listeners sparingly,Bindings express simple relationships more clearly than listeners,Use listeners for side effects and bindings for values,Create listener chains for simple computed text,"totalLabel.textProperty().bind(Bindings.format(""Total: %d"", total));","count.addListener((o, a, b) -> totalLabel.setText(""Total: "" + b));",Low,https://openjfx.io/javadoc/21/javafx.base/javafx/beans/value/ObservableValue.html
24,Events,Use action handlers for commands,Buttons and menu items should route to named command methods,Use setOnAction or @FXML handler methods with clear names,Put large lambdas inline for complex operations,"@FXML private void handleSave(ActionEvent event) { saveCustomer(); }",saveButton.setOnAction(e -> { validate(); transform(); query(); save(); refresh(); });,Medium,https://openjfx.io/javadoc/21/javafx.base/javafx/event/ActionEvent.html
25,Events,Use event filters for global shortcuts,Filters can intercept keyboard events before child controls consume them,Register accelerators or filters at Scene level,Add duplicate key handlers to every control,"scene.getAccelerators().put(new KeyCodeCombination(KeyCode.S, SHORTCUT_DOWN), this::save);",nameField.setOnKeyPressed(...); table.setOnKeyPressed(...);,Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Scene.html
26,Accessibility,Connect labels to inputs,Accessible desktop forms need labels associated with controls,Use Label.setLabelFor and clear prompt text,Use placeholder-only labels,"nameLabel.setLabelFor(nameField); nameField.setPromptText(\"Jane Doe\");",nameField.setPromptText(\"Name\");,High,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/Label.html
27,Accessibility,Expose accessible text for icon buttons,Icon-only controls need names for screen readers and tooltips,Set accessibleText and Tooltip on icon buttons,Use unlabeled graphic-only buttons,"button.setAccessibleText(""Refresh""); button.setTooltip(new Tooltip(""Refresh""));","new Button("""", refreshIcon)",High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/AccessibleRole.html
28,Accessibility,Keep keyboard focus visible,Desktop users rely on focus traversal and visible focus indicators,Preserve focus rings and tab order,Remove outlines without alternative focus state,".button:focused { -fx-border-color: -brand-focus; -fx-border-width: 2; }",".button:focused { -fx-background-insets: 0; }",High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Node.html
29,Accessibility,Use mnemonics for menu and form workflows,Mnemonics make desktop workflows faster and more accessible,Enable mnemonicParsing and choose unique mnemonic letters,Ignore keyboard alternatives for frequent actions,"saveButton.setMnemonicParsing(true); saveButton.setText(""_Save"");","saveButton.setText(""Save"");",Low,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/Labeled.html
30,Validation,Show validation near the field,Users should not hunt for form errors in desktop dialogs,Bind error labels or pseudo classes next to invalid controls,Show only a generic alert after submit,"field.pseudoClassStateChanged(PseudoClass.getPseudoClass(""invalid""), !valid);","new Alert(ERROR, ""Invalid input"").show();",Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/css/PseudoClass.html
31,Validation,Use TextFormatter for constrained input,TextFormatter prevents invalid edits before they enter the model,Attach TextFormatter for numeric dates and masks,Parse and reject invalid text only after submit,"amountField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), 0, c -> c.getControlNewText().matches(""\\d*"") ? c : null));",Integer.parseInt(amountField.getText());,Medium,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TextFormatter.html
32,Dialogs,Use modal ownership for dialogs,Dialogs should block only the relevant window and return structured results,Set owner modality and use showAndWait,Open unmanaged windows for confirmations,"dialog.initOwner(stage); dialog.initModality(Modality.WINDOW_MODAL); Optional<ButtonType> result = dialog.showAndWait();",new Stage().show();,Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/stage/Modality.html
33,Dialogs,Prefer custom DialogPane over ad hoc stages,Dialog gives consistent buttons focus and result handling,Use Dialog<T> for forms confirmations and wizards,Build every modal as a new Stage manually,"Dialog<Customer> dialog = new Dialog<>(); dialog.getDialogPane().getButtonTypes().addAll(OK, CANCEL);",Stage modal = new Stage(); modal.setScene(new Scene(new VBox()));,Low,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/Dialog.html
34,Images,Load images as resources,Packaged apps need resources resolved from the classpath or module path,Use getResourceAsStream for bundled assets,Use absolute local file paths in production UI,"new Image(getClass().getResourceAsStream(""/images/logo.png""));","new Image(""file:/Users/me/Desktop/logo.png"")",High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/image/Image.html
35,Images,Use background loading for large images,Large image decoding can pause UI startup,Use Image(url true) or a background Task for heavy assets,Load many full-size images synchronously during startup,"Image preview = new Image(url, true);",gallery.add(new Image(url));,Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/image/Image.html
36,Animation,Keep animations purposeful and short,Desktop UI animations should clarify state changes without delaying work,Use 150-250ms transitions for reveal hover and selection,Animate every layout change with long timelines,"FadeTransition ft = new FadeTransition(Duration.millis(180), pane); ft.setToValue(1);","new Timeline(new KeyFrame(Duration.seconds(2), ...)).play();",Low,https://openjfx.io/javadoc/21/javafx.graphics/javafx/animation/package-summary.html
37,Animation,Respect reduced-motion contexts where possible,Some users experience motion sensitivity in desktop apps,Provide a setting to disable decorative animations,Make animation required for comprehension,"if (settings.reducedMotion()) pane.setOpacity(1); else fade.play();",alwaysSpin.play();,Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/animation/Animation.html
38,Performance,Avoid recreating scenes for small state changes,Replacing whole scenes loses state and can flicker,Swap center content or update view models,Rebuild the entire Stage for every navigation click,"shell.setCenter(customerView);",stage.setScene(new Scene(loadMainAgain()));,Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Scene.html
39,Performance,Reuse loaded views when appropriate,FXML loading and CSS application are not free,Cache stable views or controllers for frequent navigation,Reload heavyweight screens repeatedly without need,"Node settings = viewCache.computeIfAbsent(""settings"", this::loadSettings);","button.setOnAction(e -> shell.setCenter(loadFxml(""settings.fxml"")));",Low,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html
40,Performance,Batch observable list changes,Many single-item updates can cause repeated layout and sort work,Use setAll or addAll for bulk replacement,Loop add items one by one to visible lists,"items.setAll(repository.findAll());",for(Item item : loaded) items.add(item);,Medium,https://openjfx.io/javadoc/21/javafx.base/javafx/collections/ObservableList.html
41,Architecture,Use view models for complex screens,View models keep controller state testable and separate from controls,Expose JavaFX properties from a screen model,Store all state only inside controls,"customerNameField.textProperty().bindBidirectional(viewModel.nameProperty());",String name = customerNameField.getText(); // everywhere,Medium,https://openjfx.io/javadoc/21/javafx.base/javafx/beans/property/package-summary.html
42,Architecture,Separate navigation from feature controllers,Feature controllers should not know how every screen is launched,Use a navigator or application shell service,Call FXMLLoader for unrelated screens from each controller,"navigator.showCustomers();",FXMLLoader.load(getClass().getResource(\"/views/admin.fxml\"));,Medium,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html
43,Modules,Declare required JavaFX modules,Modular JavaFX apps must require the modules they use,Add javafx.controls javafx.fxml and opens controller packages,Depend on classpath accidents only,"module app { requires javafx.controls; requires javafx.fxml; opens app.ui to javafx.fxml; }",module app { requires javafx.controls; },High,https://openjfx.io/openjfx-docs/#modular
44,Packaging,Use jlink or jpackage for desktop delivery,JavaFX apps should ship with the runtime they need,Package a runtime image or native installer,Ask end users to install matching Java and JavaFX manually,"jpackage --name MyApp --module app/app.Main --runtime-image build/image",java -jar app.jar,Medium,https://openjfx.io/openjfx-docs/#modular
45,Testing,Use TestFX for interaction tests,UI flows need automated coverage beyond controller unit tests,Write TestFX tests for key forms dialogs and navigation,Only manually click through releases,"clickOn(""#nameField"").write(""Alice""); clickOn(""Save""); verifyThat(""Saved"", isVisible());",// manual QA only,Medium,https://github.com/TestFX/TestFX
46,Theme,Use AtlantaFX as the enterprise theme baseline,AtlantaFX provides modern JavaFX themes while preserving standard controls,Use AtlantaFX user-agent stylesheet plus a small app CSS layer,Rewrite every standard control style from scratch,"Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());",scene.getStylesheets().add("/css/huge-custom-theme.css");,High,https://mkpaz.github.io/atlantafx/
47,Theme,Prefer Primer for enterprise applications,PrimerLight and PrimerDark are neutral enough for dense business workflows,Use PrimerLight as default and PrimerDark for dark mode,Use Dracula or Cupertino as the default enterprise theme,"Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());",Application.setUserAgentStylesheet(new Dracula().getUserAgentStylesheet());,Medium,https://mkpaz.github.io/atlantafx/themes/
48,Theme,Layer brand CSS after AtlantaFX,Application CSS should customize brand tokens and business states after the base theme,Add app.css to the Scene after setting AtlantaFX,Edit AtlantaFX source CSS directly,"scene.getStylesheets().add(getClass().getResource(""/css/app.css"").toExternalForm());",modify atlantafx-base CSS files,High,https://mkpaz.github.io/atlantafx/theming/
49,Theme,Use looked-up colors as enterprise tokens,JavaFX looked-up colors keep brand and semantic colors reusable across controls,Define app-primary app-success app-warning app-danger on root,Repeat hex values in every selector,".root { -app-primary: #2563eb; -app-danger: #dc2626; }",".save { -fx-background-color: #2563eb; } .link { -fx-text-fill: #2563eb; }",High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/doc-files/cssref.html
50,Theme,Keep theme switching centralized,Dark mode switching should not be scattered across controllers,Use a ThemeService that sets user-agent stylesheet and app CSS variants,Let each controller decide its own theme,themeService.apply(ThemeMode.DARK);,if(dark) button.setStyle(...);,Medium,https://mkpaz.github.io/atlantafx/
51,Theme,Validate contrast for business status colors,Enterprise screens use status colors heavily and need readable contrast,Check text on success warning danger and selected row backgrounds,Assume brand colors are accessible,".status-danger { -fx-text-fill: -app-danger; }",red text on dark red background,High,https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html
52,Theme,Use AtlantaFX style classes before custom CSS,AtlantaFX exposes utility styles that reduce custom CSS drift,Prefer Styles constants or documented style classes,Create one-off class names for every button variant,saveButton.getStyleClass().add(Styles.ACCENT);,"saveButton.getStyleClass().add(""blue-button-42"");",Medium,https://mkpaz.github.io/atlantafx/
53,Theme,Treat AtlantaFX as a base not the whole design system,AtlantaFX modernizes controls but enterprise UX still needs layout density and workflow rules,Define app shell navigation table density form and validation conventions,Assume theme choice alone solves enterprise usability,"root.getStyleClass().add(""enterprise-shell"");",only set PrimerLight and stop,High,https://mkpaz.github.io/atlantafx/
54,Icons,Use Ikonli for consistent enterprise icons,Icon fonts integrate cleanly with JavaFX controls and avoid emoji-style UI,Use FontIcon with semantic style classes,Use emoji as toolbar or menu icons,"Button refresh = new Button(""Refresh"", new FontIcon(""mdi2r-refresh""));",new Button("Refresh"),Medium,https://kordamp.org/ikonli/
55,Components,Use AtlantaFX controls for common app affordances,AtlantaFX provides useful controls such as Card Message ModalPane Popover and ToggleSwitch,Use built-in AtlantaFX controls before adding another dependency,Add ControlsFX for components AtlantaFX already covers,"Message message = new Message(""Saved"", ""Customer updated successfully"");","new Label(""Saved"") with ad hoc styling",Medium,https://mkpaz.github.io/atlantafx/
56,Components,Add ControlsFX only for missing enterprise controls,ControlsFX is useful for specialized controls but should stay optional,Use ControlsFX for SpreadsheetView PropertySheet CheckComboBox or StatusBar needs,Add ControlsFX by default before requirements are clear,PropertySheet sheet = new PropertySheet(items);,"implementation ""org.controlsfx:controlsfx"" with no usage",Low,https://controlsfx.github.io/
57,Testing,Test theme-critical flows with TestFX,Theme and CSS changes can break focus visibility dialogs and button affordance,Use TestFX for login save validation and modal workflows,Only inspect AtlantaFX screens manually,"clickOn(""#saveButton""); verifyThat("".message"", isVisible());",manual theme QA only,Medium,https://github.com/TestFX/TestFX
58,Architecture,Use application shell plus feature workspaces,Enterprise JavaFX apps need stable navigation around changing work areas,Use BorderPane shell with navigation toolbar and central workspace,Replace the whole Stage for every feature,"shell.setLeft(navigation); shell.setTop(toolbar); shell.setCenter(workspace);",stage.setScene(new Scene(loadFeature()));,High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/BorderPane.html
59,Architecture,Use MVVM for complex enterprise screens,Large forms and tables need testable state outside the controller,Expose JavaFX properties from view models and bind controls to them,Put all screen state and validation in the controller,amountField.textProperty().bindBidirectional(vm.amountProperty());,controller.amount = amountField.getText();,High,https://openjfx.io/javadoc/21/javafx.base/javafx/beans/property/package-summary.html
60,Architecture,Inject services into controllers,Enterprise controllers should coordinate UI and call application services,Use a controller factory or DI container for services,Create database connections inside FXML controllers,loader.setControllerFactory(type -> injector.getInstance(type));,new CustomerRepository(new DriverManager(...)),High,https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html
61,Navigation,Use role-aware navigation models,Menus toolbars and shortcuts should reflect the same permission model,Build navigation items from commands with required roles,Hide buttons in one place and leave shortcuts enabled,"command.enabledProperty().bind(permissionService.allowed(""invoice.approve""));",approveButton.setVisible(false);,High,
62,Workflow,Represent workflow states visibly,Approval and processing screens need clear business state signals,Use semantic badges row styles and disabled actions by workflow state,Use only free text status columns,"row.pseudoClassStateChanged(PseudoClass.getPseudoClass(""blocked""), item.isBlocked());","statusCol.setText(""B"");",Medium,https://openjfx.io/javadoc/21/javafx.graphics/javafx/css/PseudoClass.html
63,TableView,Design TableView for high-density enterprise data,Enterprise users scan compare sort filter and act on rows for long periods,Use compact row height clear columns sorting filtering and selection summary,Use card grids for large tabular datasets,"table.getStyleClass().add(""dense-table""); table.getSortOrder().setAll(updatedAtCol);",new TilePane(customerCards),High,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TableView.html
64,TableView,Keep row actions predictable,Inline actions in dense tables should be limited and permission-aware,Use context menus or a side detail panel for secondary actions,Place many buttons in every row,"table.setRowFactory(tv -> { TableRow<Order> row = new TableRow<>(); row.setContextMenu(orderMenu); return row; });",row contains Edit Delete Approve Print Email buttons,Medium,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/ContextMenu.html
65,TableView,Use server-side paging for large enterprise datasets,Desktop clients should not load entire enterprise tables into memory,Fetch pages or filtered slices from services,Load all records and filter in the UI,"Page<Customer> page = customerService.search(criteria, pageRequest);",customerRepository.findAll(),High,
66,Forms,Use form sections for enterprise data entry,Long enterprise forms need grouping and progressive disclosure,Group fields into titled sections with validation summaries,Place dozens of inputs in one unbroken GridPane,"TitledPane billing = new TitledPane(""Billing"", billingForm);",new GridPane with 80 controls,Medium,https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TitledPane.html
67,Forms,Provide validation summary plus field errors,Enterprise forms often need multiple corrections before submission,Show a summary at top and field-level messages near controls,Show only one modal alert after Save,"summary.setItems(vm.validationErrors()); field.pseudoClassStateChanged(INVALID, fieldError);","new Alert(ERROR, ""Invalid form"").showAndWait();",High,
68,Tasks,Make long operations cancellable,Enterprise imports exports sync and reports need cancel paths,Expose cancel button bound to Task running state,Force users to wait or kill the app,cancelButton.setOnAction(e -> task.cancel());,runReportButton.setDisable(true);,High,https://openjfx.io/javadoc/21/javafx.graphics/javafx/concurrent/Task.html
69,Tasks,Surface retryable errors without losing context,Network and service failures should preserve user input and next action,Show inline retry messages and keep form/table state,Clear the screen on service failure,"message.setDescription(""Could not save. Check connection and retry."");",loadErrorScene();,High,
70,Audit,Log business actions through services,Enterprise desktop apps need traceability for sensitive changes,Record user action entity result and timestamp in service layer,Log only UI button clicks,"audit.log(user, ""invoice.approve"", invoiceId, SUCCESS);","System.out.println(""clicked approve"");",Medium,
71,Configuration,Separate user preferences from application config,Enterprise apps need deploy-time config and per-user preferences,Use config files for endpoints and Preferences for UI choices,Hard-code environment URLs and window state,"Preferences.userNodeForPackage(App.class).put(""theme"", ""dark"");","private static final String API = ""http://localhost:8080"";",Medium,https://docs.oracle.com/en/java/javase/21/docs/api/java.prefs/java/util/prefs/Preferences.html
72,Deployment,Package resources and themes inside the runtime image,AtlantaFX app CSS icons and FXML must be available after jpackage,Load resources from classpath or module resources,Load theme files from developer machine paths,"getClass().getResource(""/css/app.css"").toExternalForm();","new File(""src/main/resources/css/app.css"").toURI()",High,https://openjfx.io/openjfx-docs/#modular
73,Deployment,Write logs to user-writable locations,Installed desktop apps may not write inside the application directory,Use platform-specific user data directories for logs and cache,Write logs beside the executable,"Path logs = appData.resolve(""logs/app.log"");",Path.of("app.log"),Medium,
74,Testing,Cover enterprise happy path and failure path,Enterprise UI tests should verify save validation permission and service failure flows,Use TestFX for core workflows and service fakes,Only test controller methods without UI interaction,"clickOn(""Save""); verifyThat(""Customer saved"", isVisible());",controller.save(); assertTrue(saved);,High,https://github.com/TestFX/TestFX
75,Dependencies,Keep optional UI libraries behind actual needs,AtlantaFX should be default but additional libraries should be justified,Start with JavaFX AtlantaFX Ikonli TestFX and add ControlsFX only for missing controls,Adopt many UI libraries at project start,"dependencies { implementation(""io.github.mkpaz:atlantafx-base:2.1.0"") }",implementation controlsfx gemsfx tilesfx materialfx all at once,Medium,
Can't render this file because it contains an unexpected character in line 4 and column 345.

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Composable,Pure UI composables,Composable functions should only render UI,Accept state and callbacks,Calling usecase/repo,Pure UI composable,Business logic in UI,High,https://developer.android.com/jetpack/compose/mental-model
2,Composable,Small composables,Each composable has single responsibility,Split into components,Huge composable,Reusable UI,Monolithic UI,Medium,
3,Composable,Stateless by default,Prefer stateless composables,Hoist state,Local mutable state,Stateless UI,Hidden state,High,https://developer.android.com/jetpack/compose/state#state-hoisting
4,State,Single source of truth,UI state comes from one source,StateFlow from VM,Multiple states,Unified UiState,Scattered state,High,https://developer.android.com/topic/architecture/ui-layer
5,State,Model UI State,Use sealed interface/data class,UiState.Loading,Boolean flags,Explicit state,Flag hell,High,
6,State,remember only UI state,remember for UI-only state,"Scroll, animation",Business state,Correct remember,Misuse remember,High,https://developer.android.com/jetpack/compose/state
7,State,rememberSaveable,Persist state across config,rememberSaveable,remember,State survives,State lost,High,https://developer.android.com/jetpack/compose/state#restore-ui-state
8,State,derivedStateOf,Optimize recomposition,derivedStateOf,Recompute always,Optimized,Jank,Medium,https://developer.android.com/jetpack/compose/performance
9,SideEffect,LaunchedEffect keys,Use correct keys,LaunchedEffect(id),LaunchedEffect(Unit),Scoped effect,Infinite loop,High,https://developer.android.com/jetpack/compose/side-effects
10,SideEffect,rememberUpdatedState,Avoid stale lambdas,rememberUpdatedState,Capture directly,Safe callback,Stale state,Medium,https://developer.android.com/jetpack/compose/side-effects
11,SideEffect,DisposableEffect,Clean up resources,onDispose,No cleanup,No leak,Memory leak,High,
12,Architecture,Unidirectional data flow,UI → VM → State,onEvent,Two-way binding,Predictable flow,Hard debug,High,https://developer.android.com/topic/architecture
13,Architecture,No business logic in UI,Logic belongs to VM,Collect state,Call repo,Clean UI,Fat UI,High,
14,Architecture,Expose immutable state,Expose StateFlow,asStateFlow,Mutable exposed,Safe API,State mutation,High,
15,Lifecycle,Lifecycle-aware collect,Use collectAsStateWithLifecycle,Lifecycle aware,collectAsState,No leak,Leak,High,https://developer.android.com/jetpack/compose/lifecycle
16,Navigation,Event-based navigation,VM emits navigation event,"VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate",Nav in UI,Decoupled nav,Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow),High,https://developer.android.com/jetpack/compose/navigation
17,Navigation,Typed routes,Use sealed routes,sealed class Route,String routes,Type-safe,Runtime crash,Medium,
18,Performance,Stable parameters,Prefer immutable/stable params,@Immutable,Mutable params,Stable recomposition,Extra recomposition,High,https://developer.android.com/jetpack/compose/performance
19,Performance,Use key in Lazy,Provide stable keys,key=id,No key,Stable list,Item jump,High,
20,Performance,Avoid heavy work,No heavy computation in UI,Precompute in VM,Compute in UI,Smooth UI,Jank,High,
21,Performance,Remember expensive objects,remember heavy objects,remember,Recreate each recomposition,Efficient,Wasteful,Medium,
22,Theming,Design system,Centralized theme,Material3 tokens,Hardcoded values,Consistent UI,Inconsistent,High,https://developer.android.com/jetpack/compose/themes
23,Theming,Dark mode support,Theme-based colors,colorScheme,Fixed color,Adaptive UI,Broken dark,Medium,
24,Layout,Prefer Modifier over extra layouts,Use Modifier to adjust layout instead of adding wrapper composables,Use Modifier.padding(),Wrap content with extra Box,Padding via modifier,Box just for padding,High,https://developer.android.com/jetpack/compose/modifiers
25,Layout,Avoid deep layout nesting,Deep layout trees increase measure & layout cost,Keep layout flat,Box ? Column ? Box ? Row,Flat hierarchy,Deep nested tree,High,
26,Layout,Use Row/Column for linear layout,Linear layouts are simpler and more performant,Use Row / Column,Custom layout for simple cases,Row/Column usage,Over-engineered layout,High,
27,Layout,Use Box only for overlapping content,Box should be used only when children overlap,Stack elements,Use Box as Column,Proper overlay,Misused Box,Medium,
28,Layout,Prefer LazyColumn over Column scroll,Lazy layouts are virtualized and efficient,LazyColumn,Column.verticalScroll(),Lazy list,Scrollable Column,High,https://developer.android.com/jetpack/compose/lists
29,Layout,Avoid nested scroll containers,Nested scrolling causes UX & performance issues,Single scroll container,Scroll inside scroll,One scroll per screen,Nested scroll,High,
30,Layout,Avoid fillMaxSize by default,fillMaxSize may break parent constraints,Use exact size,Fill max everywhere,Constraint-aware size,Overfilled layout,Medium,
31,Layout,Avoid intrinsic size unless necessary,Intrinsic measurement is expensive,Explicit sizing,IntrinsicSize.Min,Predictable layout,Expensive measure,High,https://developer.android.com/jetpack/compose/layout/intrinsics
32,Layout,Use Arrangement and Alignment APIs,Declare layout intent explicitly,Use Arrangement / Alignment,Manual spacing hacks,Declarative spacing,Magic spacing,High,
33,Layout,Extract reusable layout patterns,Repeated layouts should be shared,Create layout composable,Copy-paste layouts,Reusable scaffold,Duplicated layout,High,
34,Theming,No hardcoded text style,Use typography,MaterialTheme.typography,Hardcode sp,Scalable,Inconsistent,Medium,
35,Testing,Stateless UI testing,Composable easy to test,Pass state,Hidden state,Testable,Hard test,High,https://developer.android.com/jetpack/compose/testing
36,Testing,Use testTag,Stable UI selectors,Modifier.testTag,Find by text,Stable tests,Flaky tests,Medium,
37,Preview,Multiple previews,Preview multiple states,@Preview,Single preview,Better dev UX,Misleading,Low,https://developer.android.com/jetpack/compose/tooling/preview
38,DI,Inject VM via Hilt,Use hiltViewModel,@HiltViewModel,Manual VM,Clean DI,Coupling,High,https://developer.android.com/training/dependency-injection/hilt-jetpack
39,DI,No DI in UI,Inject in VM,Constructor inject,Inject composable,Proper scope,Wrong scope,High,
40,Accessibility,Content description,Accessible UI,contentDescription,Ignore a11y,Inclusive,A11y fail,Medium,https://developer.android.com/jetpack/compose/accessibility
41,Accessibility,Semantics,Use semantics API,Modifier.semantics,None,Testable a11y,Invisible,Medium,
42,Animation,Compose animation APIs,Use animate*AsState,AnimatedVisibility,Manual anim,Smooth,Jank,Medium,https://developer.android.com/jetpack/compose/animation
43,Animation,Avoid animation logic in VM,Animation is UI concern,Animate in UI,Animate in VM,Correct layering,Mixed concern,Low,
44,Modularization,Feature-based UI modules,UI per feature,:feature:ui,God module,Scalable,Tight coupling,High,https://developer.android.com/topic/modularization
45,Modularization,Public UI contracts,Expose minimal UI API,Interface/Route,Expose impl,Encapsulated,Leaky module,Medium,
46,State,Snapshot state only,Use Compose state,mutableStateOf,Custom observable,Compose aware,Buggy UI,Medium,
47,State,Avoid mutable collections,Immutable list/map,PersistentList,MutableList,Stable UI,Silent bug,High,
48,Lifecycle,RememberCoroutineScope usage,Only for UI jobs,UI coroutine,Long jobs,Scoped job,Leak,Medium,https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
49,Interop,Interop View carefully,Use AndroidView,Isolated usage,Mix everywhere,Safe interop,Messy UI,Low,https://developer.android.com/jetpack/compose/interop
50,Interop,Avoid legacy patterns,No LiveData in UI,StateFlow,LiveData,Modern stack,Legacy debt,Medium,
51,Debug,Use layout inspector,Inspect recomposition,Tools,Blind debug,Fast debug,Guessing,Low,https://developer.android.com/studio/debug/layout-inspector
52,Debug,Enable recomposition counts,Track recomposition,Debug flags,Ignore,Performance aware,Hidden jank,Low,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Composable Pure UI composables Composable functions should only render UI Accept state and callbacks Calling usecase/repo Pure UI composable Business logic in UI High https://developer.android.com/jetpack/compose/mental-model
3 2 Composable Small composables Each composable has single responsibility Split into components Huge composable Reusable UI Monolithic UI Medium
4 3 Composable Stateless by default Prefer stateless composables Hoist state Local mutable state Stateless UI Hidden state High https://developer.android.com/jetpack/compose/state#state-hoisting
5 4 State Single source of truth UI state comes from one source StateFlow from VM Multiple states Unified UiState Scattered state High https://developer.android.com/topic/architecture/ui-layer
6 5 State Model UI State Use sealed interface/data class UiState.Loading Boolean flags Explicit state Flag hell High
7 6 State remember only UI state remember for UI-only state Scroll, animation Business state Correct remember Misuse remember High https://developer.android.com/jetpack/compose/state
8 7 State rememberSaveable Persist state across config rememberSaveable remember State survives State lost High https://developer.android.com/jetpack/compose/state#restore-ui-state
9 8 State derivedStateOf Optimize recomposition derivedStateOf Recompute always Optimized Jank Medium https://developer.android.com/jetpack/compose/performance
10 9 SideEffect LaunchedEffect keys Use correct keys LaunchedEffect(id) LaunchedEffect(Unit) Scoped effect Infinite loop High https://developer.android.com/jetpack/compose/side-effects
11 10 SideEffect rememberUpdatedState Avoid stale lambdas rememberUpdatedState Capture directly Safe callback Stale state Medium https://developer.android.com/jetpack/compose/side-effects
12 11 SideEffect DisposableEffect Clean up resources onDispose No cleanup No leak Memory leak High
13 12 Architecture Unidirectional data flow UI → VM → State onEvent Two-way binding Predictable flow Hard debug High https://developer.android.com/topic/architecture
14 13 Architecture No business logic in UI Logic belongs to VM Collect state Call repo Clean UI Fat UI High
15 14 Architecture Expose immutable state Expose StateFlow asStateFlow Mutable exposed Safe API State mutation High
16 15 Lifecycle Lifecycle-aware collect Use collectAsStateWithLifecycle Lifecycle aware collectAsState No leak Leak High https://developer.android.com/jetpack/compose/lifecycle
17 16 Navigation Event-based navigation VM emits navigation event VM: Channel + receiveAsFlow(), V: Collect with Dispatchers.Main.immediate Nav in UI Decoupled nav Using State / SharedFlow for navigation -> event is replayed and navigation fires again (StateFlow) High https://developer.android.com/jetpack/compose/navigation
18 17 Navigation Typed routes Use sealed routes sealed class Route String routes Type-safe Runtime crash Medium
19 18 Performance Stable parameters Prefer immutable/stable params @Immutable Mutable params Stable recomposition Extra recomposition High https://developer.android.com/jetpack/compose/performance
20 19 Performance Use key in Lazy Provide stable keys key=id No key Stable list Item jump High
21 20 Performance Avoid heavy work No heavy computation in UI Precompute in VM Compute in UI Smooth UI Jank High
22 21 Performance Remember expensive objects remember heavy objects remember Recreate each recomposition Efficient Wasteful Medium
23 22 Theming Design system Centralized theme Material3 tokens Hardcoded values Consistent UI Inconsistent High https://developer.android.com/jetpack/compose/themes
24 23 Theming Dark mode support Theme-based colors colorScheme Fixed color Adaptive UI Broken dark Medium
25 24 Layout Prefer Modifier over extra layouts Use Modifier to adjust layout instead of adding wrapper composables Use Modifier.padding() Wrap content with extra Box Padding via modifier Box just for padding High https://developer.android.com/jetpack/compose/modifiers
26 25 Layout Avoid deep layout nesting Deep layout trees increase measure & layout cost Keep layout flat Box ? Column ? Box ? Row Flat hierarchy Deep nested tree High
27 26 Layout Use Row/Column for linear layout Linear layouts are simpler and more performant Use Row / Column Custom layout for simple cases Row/Column usage Over-engineered layout High
28 27 Layout Use Box only for overlapping content Box should be used only when children overlap Stack elements Use Box as Column Proper overlay Misused Box Medium
29 28 Layout Prefer LazyColumn over Column scroll Lazy layouts are virtualized and efficient LazyColumn Column.verticalScroll() Lazy list Scrollable Column High https://developer.android.com/jetpack/compose/lists
30 29 Layout Avoid nested scroll containers Nested scrolling causes UX & performance issues Single scroll container Scroll inside scroll One scroll per screen Nested scroll High
31 30 Layout Avoid fillMaxSize by default fillMaxSize may break parent constraints Use exact size Fill max everywhere Constraint-aware size Overfilled layout Medium
32 31 Layout Avoid intrinsic size unless necessary Intrinsic measurement is expensive Explicit sizing IntrinsicSize.Min Predictable layout Expensive measure High https://developer.android.com/jetpack/compose/layout/intrinsics
33 32 Layout Use Arrangement and Alignment APIs Declare layout intent explicitly Use Arrangement / Alignment Manual spacing hacks Declarative spacing Magic spacing High
34 33 Layout Extract reusable layout patterns Repeated layouts should be shared Create layout composable Copy-paste layouts Reusable scaffold Duplicated layout High
35 34 Theming No hardcoded text style Use typography MaterialTheme.typography Hardcode sp Scalable Inconsistent Medium
36 35 Testing Stateless UI testing Composable easy to test Pass state Hidden state Testable Hard test High https://developer.android.com/jetpack/compose/testing
37 36 Testing Use testTag Stable UI selectors Modifier.testTag Find by text Stable tests Flaky tests Medium
38 37 Preview Multiple previews Preview multiple states @Preview Single preview Better dev UX Misleading Low https://developer.android.com/jetpack/compose/tooling/preview
39 38 DI Inject VM via Hilt Use hiltViewModel @HiltViewModel Manual VM Clean DI Coupling High https://developer.android.com/training/dependency-injection/hilt-jetpack
40 39 DI No DI in UI Inject in VM Constructor inject Inject composable Proper scope Wrong scope High
41 40 Accessibility Content description Accessible UI contentDescription Ignore a11y Inclusive A11y fail Medium https://developer.android.com/jetpack/compose/accessibility
42 41 Accessibility Semantics Use semantics API Modifier.semantics None Testable a11y Invisible Medium
43 42 Animation Compose animation APIs Use animate*AsState AnimatedVisibility Manual anim Smooth Jank Medium https://developer.android.com/jetpack/compose/animation
44 43 Animation Avoid animation logic in VM Animation is UI concern Animate in UI Animate in VM Correct layering Mixed concern Low
45 44 Modularization Feature-based UI modules UI per feature :feature:ui God module Scalable Tight coupling High https://developer.android.com/topic/modularization
46 45 Modularization Public UI contracts Expose minimal UI API Interface/Route Expose impl Encapsulated Leaky module Medium
47 46 State Snapshot state only Use Compose state mutableStateOf Custom observable Compose aware Buggy UI Medium
48 47 State Avoid mutable collections Immutable list/map PersistentList MutableList Stable UI Silent bug High
49 48 Lifecycle RememberCoroutineScope usage Only for UI jobs UI coroutine Long jobs Scoped job Leak Medium https://developer.android.com/jetpack/compose/side-effects#remembercoroutinescope
50 49 Interop Interop View carefully Use AndroidView Isolated usage Mix everywhere Safe interop Messy UI Low https://developer.android.com/jetpack/compose/interop
51 50 Interop Avoid legacy patterns No LiveData in UI StateFlow LiveData Modern stack Legacy debt Medium
52 51 Debug Use layout inspector Inspect recomposition Tools Blind debug Fast debug Guessing Low https://developer.android.com/studio/debug/layout-inspector
53 52 Debug Enable recomposition counts Track recomposition Debug flags Ignore Performance aware Hidden jank Low

View File

@@ -0,0 +1,51 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Blade Templates,Use Blade components for reusable UI,Extract repeated markup into named Blade components,Use x-* components with @props for all reusable UI,Duplicate HTML blocks across views,<x-card :title="$title">{{ $slot }}</x-card>,@include('card' ['title' => $title]),High,https://laravel.com/docs/blade#components
2,Blade Templates,Use layouts with @extends and @section,Define one master layout and extend it per page,@extends layout with named @section blocks,Duplicate header/footer HTML in every view,@extends('layouts.app') @section('content'),Full HTML in every view file,High,https://laravel.com/docs/blade#layouts-using-template-inheritance
3,Blade Templates,Use @props for component type-safety,Declare accepted props inside components with @props,@props with defaults to document component API,Pass arbitrary variables without declaration,@props(['title' => '' 'variant' => 'primary']),No @props declaration in component,Medium,https://laravel.com/docs/blade#component-data-and-attributes
4,Blade Templates,Use conditional CSS classes with @class,Build class strings conditionally without ternary noise,@class directive for conditional class binding,String concatenation or nested ternaries,@class(['btn' 'btn-primary' => $primary 'btn-disabled' => $disabled]),class="btn {{ $primary ? 'btn-primary' : '' }}",Medium,https://laravel.com/docs/blade#conditional-classes-and-styles
5,Blade Templates,Use named slots for flexible layouts,Named slots let callers inject content into specific regions,@slot('header') and $slot for flexible component APIs,Hard-code all sub-sections inside components,"<x-modal><x-slot:header>Title</x-slot>Body</x-modal>",<x-modal title="Title">Body with no slot control</x-modal>,Medium,https://laravel.com/docs/blade#slots
6,Blade Templates,Use Blade directives instead of raw PHP,Blade directives are readable and IDE-supported,@if @foreach @forelse @empty instead of <?php ?>,Raw PHP tags inside Blade templates,@forelse($items as $item) ... @empty <p>None</p> @endforelse,<?php foreach($items as $item): ?>,High,https://laravel.com/docs/blade#blade-directives
7,Blade Templates,Escape output with {{ }},Use double curly braces for XSS-safe output,{{ }} for all user-supplied or dynamic text,{!! !!} for untrusted data,{{ $user->name }},{!! $user->name !!},High,https://laravel.com/docs/blade#displaying-data
8,Blade Templates,Use @vite for asset loading,Vite integration handles cache busting and HMR automatically,@vite(['resources/css/app.css' 'resources/js/app.js']),Manual script/link tags with hardcoded paths,@vite(['resources/css/app.css' 'resources/js/app.js']),<link href="/css/app.css?v=123">,High,https://laravel.com/docs/vite
9,Livewire,Bind inputs with wire:model,Two-way data binding keeps component state in sync,wire:model for all form inputs managed by Livewire,Manual JavaScript listeners syncing to component,<input wire:model="email">,<input @change="$wire.email = $event.target.value">,High,https://livewire.laravel.com/docs/properties
10,Livewire,Use wire:model.live for real-time validation,Validate on input rather than only on submit,wire:model.live + #[Validate] for instant feedback,Only validate on form submit,<input wire:model.live="email"> with #[Validate('email')],<input wire:model="email"> with validate() on submit only,Medium,https://livewire.laravel.com/docs/validation
11,Livewire,Use wire:click for actions,Bind UI events to component methods cleanly,wire:click for buttons and interactive elements,JavaScript fetch calls replicating Livewire actions,<button wire:click="save">Save</button>,<button onclick="fetch('/save')">Save</button>,High,https://livewire.laravel.com/docs/actions
12,Livewire,Use lifecycle hooks appropriately,mount() for init; updated() for reactive side effects,mount() for initialization updatedFoo() for property changes,Heavy logic in render() or __construct(),public function mount(): void { $this->items = Item::all(); },public function render(): View { $this->items = Item::all(); },Medium,https://livewire.laravel.com/docs/lifecycle-hooks
13,Livewire,Use lazy loading for heavy components,Defer render of expensive components until visible,wire:init or lazy attribute on components,Load all Livewire components on page load,<livewire:analytics-chart lazy />,<livewire:analytics-chart /> with heavy DB queries on mount,Medium,https://livewire.laravel.com/docs/lazy
14,Livewire,Integrate Alpine.js for local UI state,Use Alpine.js for UI-only state that doesn't need server round-trips,x-data / x-show / x-transition for tooltips dropdowns,Livewire server calls for purely visual toggle state,<div x-data="{ open: false }"><button @click="open = !open">,<button wire:click="toggleDropdown"> for a local dropdown,Medium,https://livewire.laravel.com/docs/alpine
15,Livewire,Use wire:loading for feedback,Always indicate to users when a server action is in progress,wire:loading.attr="disabled" and wire:loading elements,Provide no feedback while Livewire request is in flight,<button wire:click="save" wire:loading.attr="disabled">Save</button>,<button wire:click="save">Save</button> with no loading state,High,https://livewire.laravel.com/docs/wire-loading
16,Livewire,Handle file uploads with WithFileUploads,Livewire's trait manages chunked upload and temp storage,WithFileUploads trait + wire:model for file inputs,Manual multipart form submissions for Livewire pages,use WithFileUploads; public $photo; <input wire:model="photo" type="file">,<form action="/upload" method="POST" enctype="multipart/form-data">,Medium,https://livewire.laravel.com/docs/uploads
17,Inertia.js,Use Inertia page components as route endpoints,Each page is a Vue/React component rendered server-side via Inertia::render(),Inertia::render('Dashboard' ['data' => $data]) in controllers,Return JSON and fetch from JavaScript,return Inertia::render('Users/Index' ['users' => $users]);,return response()->json($users); with client-side fetch,High,https://inertiajs.com/responses
18,Inertia.js,Share global data via HandleInertiaRequests,Middleware share() provides auth user and flash to every page,Share auth/flash in HandleInertiaRequests middleware,Pass auth to every Inertia::render() call,public function share(Request $r): array { return ['auth' => ['user' => $r->user()]]; },Inertia::render('Page' ['auth' => auth()->user()]) every controller,High,https://inertiajs.com/shared-data
19,Inertia.js,Use <Link> for client-side navigation,Inertia Link intercepts clicks for SPA-like transitions,<Link href="/dashboard"> instead of <a href>,Regular <a> tags for internal navigation,<Link href={route('dashboard')}>Dashboard</Link>,<a href="/dashboard">Dashboard</a>,High,https://inertiajs.com/links
20,Inertia.js,Use useForm for form state and submission,Inertia's useForm manages progress errors and transforms,"useForm for all page-level forms, form.post() for submit",Axios/fetch for form submissions on Inertia pages,"const form = useForm({ name: '' }); form.post('/users');","axios.post('/users', { name });",High,https://inertiajs.com/forms
21,Inertia.js,Use persistent layouts to preserve state,Wrap pages in a persistent layout so header/sidebar don't remount,layout property on page component for persistent UI,Re-render full layout on every page visit,MyPage.layout = (page) => <AppLayout>{page}</AppLayout>,No layout — full page reload feel on navigation,Medium,https://inertiajs.com/pages#persistent-layouts
22,Inertia.js,Enable SSR for public pages,Server-side rendering improves SEO and first paint,Enable Inertia SSR for marketing and public pages,Client-only rendering for all pages including public,php artisan inertia:start-ssr with @inertiaHead,No SSR on pages requiring good SEO,Medium,https://inertiajs.com/server-side-rendering
23,Styling,Set up Tailwind CSS via Vite,Use Vite + tailwindcss plugin for fast HMR and optimized builds,Install tailwindcss @tailwindcss/vite and configure vite.config.js,Laravel Mix or manual PostCSS pipeline for new projects,plugins: [tailwindcss()] in vite.config.js + @import 'tailwindcss' in app.css,Laravel Mix with require('tailwindcss') in webpack,High,https://tailwindcss.com/docs/installation/framework-guides
24,Styling,Purge unused styles via content config,Tailwind scans Blade and JS files to tree-shake unused classes,content: ['./resources/views/**/*.blade.php' './resources/js/**/*.{js,vue}'],No content config — ship all 3MB of CSS,content: ['./resources/**/*.blade.php' './resources/**/*.js'],content: [],High,https://tailwindcss.com/docs/content-configuration
25,Styling,Use dark mode class strategy,class-based dark mode integrates with server-rendered preference,darkMode: 'class' with a toggle that sets class on <html>,Media query only — no user override possible,darkMode: 'class'; document.documentElement.classList.toggle('dark'),darkMode: 'media' — no programmatic control,Medium,https://tailwindcss.com/docs/dark-mode
26,Styling,Use @apply sparingly in component CSS,Extract only truly repeated multi-class patterns,@apply for BEM base classes shared across many components,@apply for every single element — defeats Tailwind's purpose,@apply flex items-center gap-2 (shared button base),@apply text-sm for a single use,Low,https://tailwindcss.com/docs/functions-and-directives#apply
27,Styling,Configure custom design tokens in CSS,Define brand colors spacing fonts as CSS variables consumed by Tailwind,Custom @theme tokens matched to brand guidelines,Magic color hex codes scattered across Blade templates,@theme { --color-brand: oklch(0.6 0.2 250); },bg-[#1a2b3c] inline throughout templates,Medium,https://tailwindcss.com/docs/theme
28,Components,Use anonymous Blade components for UI primitives,Blade files in resources/views/components/ auto-register as x-* components,Anonymous components for buttons alerts badges cards,Blade @includes for anything reusable,<x-badge variant="success">Active</x-badge>,@include('partials.badge' ['variant' => 'success']),Medium,https://laravel.com/docs/blade#anonymous-components
29,Components,Use class-based components for complex logic,PHP class components can inject services and pre-process data,app/View/Components/ class when component needs PHP logic,Blade @php blocks for business logic inside templates,class AlertComponent { public function __construct(public string $type) {} },@php $color = $type === 'error' ? 'red' : 'green'; @endphp,Medium,https://laravel.com/docs/blade#components
30,Components,Forward extra attributes with $attributes,Pass through HTML attributes like class id aria to root element,$attributes->merge() on root element of components,Ignore caller-provided HTML attributes silently,<div {{ $attributes->merge(['class' => 'btn']) }}>,<div class="btn"> — drops extra class/id from caller,High,https://laravel.com/docs/blade#component-attributes
31,Components,Separate variant logic from templates,Keep variant/size/color logic in a PHP class or helper not in Blade,Variant class or match() expression in component class,Long @if chains for variants inside Blade templates,"public function classes(): string { return match($this->variant) { 'primary' => 'bg-blue-600', } }","@if($variant === 'primary') bg-blue-600 @elseif($variant === 'secondary')...",Medium,https://laravel.com/docs/blade#components
32,Components,Provide default slot content,Use {{ $slot ?? '' }} or named slot defaults so components are usable empty,Default content in slots for optional regions,Require every slot to be filled — throws errors on empty usage,{{ $icon ?? '' }} in component Blade file,{{ $icon }} — fatal if caller omits slot,Low,https://laravel.com/docs/blade#slots
33,Components,Use component namespacing for packages,Prefix third-party or module components to avoid collisions,Register custom prefix via Blade::componentNamespace(),Mix first-party and package component names with no prefix,Blade::componentNamespace('Modules\\Shop\\Views' 'shop'); <x-shop::product-card />,<x-product-card /> colliding with first-party card,Low,https://laravel.com/docs/blade#manually-registering-components
34,Forms,Validate with Form Request classes,Move validation rules out of controllers into dedicated FormRequest classes,php artisan make:request and define rules() + authorize(),Inline validate() in controller actions,class StorePostRequest extends FormRequest { public function rules() { return ['title' => 'required|max:255']; } },public function store(Request $r) { $r->validate(['title' => 'required']); },High,https://laravel.com/docs/validation#form-request-validation
35,Forms,Preserve old input on validation failure,Use old() to repopulate form fields after server-side error redirect,old('field') as default value on all form inputs,Empty form fields when validation fails,<input name="email" value="{{ old('email') }}">,<input name="email">,High,https://laravel.com/docs/validation#repopulating-forms
36,Forms,Display validation errors with @error,Use the @error directive for inline field-level error messages,@error('field') to show per-field messages,Dump $errors->all() in one block at top of form,@error('email') <p class="text-red-500">{{ $message }}</p> @enderror,@foreach($errors->all() as $e) {{ $e }} @endforeach,Medium,https://laravel.com/docs/validation#quick-displaying-the-validation-errors
37,Forms,Use CSRF token on all forms,CSRF protection is enabled by default — include @csrf in every form,@csrf in every POST/PUT/PATCH/DELETE form,Disable VerifyCsrfToken middleware for convenience,<form method="POST">@csrf ...,<form method="POST"> without @csrf,High,https://laravel.com/docs/csrf
38,Forms,Use method spoofing for PUT/PATCH/DELETE,HTML forms only support GET/POST — use @method for REST actions,@method('PUT') inside form for update/delete routes,Route::post for all mutations including updates,"<form method=""POST"">@csrf @method('PUT')",<form method="POST" action="/users/update">,Medium,https://laravel.com/docs/routing#form-method-spoofing
39,Forms,Display flash messages consistently,Flash success/error in controller; read in layout with session(),session('status') in layout for global flash display,Re-query DB or pass flash from every controller individually,@if(session('success')) <div class="alert">{{ session('success') }}</div> @endif,if($user) return back()->with(['user' => $user]);,Medium,https://laravel.com/docs/session#flash-data
40,Performance,Eager load relationships to prevent N+1,Always eager load related models used in views with with(),with() in queries before passing collections to views,Lazy-load relations inside Blade loops,User::with('posts' 'avatar')->get(),User::all() then @foreach $user->posts in Blade,High,https://laravel.com/docs/eloquent-relationships#eager-loading
41,Performance,Cache rendered Blade fragments,Use cache() helper to wrap expensive rendered partials,cache() around slow partials that change infrequently,Re-render identical content on every request,@php echo cache()->remember('sidebar' 3600 fn() => view('sidebar')->render()); @endphp,{{ view('sidebar')->render() }} on every page load,Medium,https://laravel.com/docs/cache
42,Performance,Paginate large data sets,Always paginate collections in list views,->paginate() or ->simplePaginate() with {{ $items->links() }},->get() for large tables in views,User::paginate(20) with <x-pagination :links="$users" />,User::all() passed to Blade,High,https://laravel.com/docs/pagination
43,Performance,Queue slow background tasks,Offload emails notifications and heavy processing to queues,Dispatch jobs for anything taking >200ms,Block HTTP request with slow operations,ProcessImage::dispatch($file); return back();,Storage::put(); Mail::send(); Image::resize(); in controller,High,https://laravel.com/docs/queues
44,Performance,Use route model binding,Laravel resolves models automatically — avoids manual find(),Type-hint model in controller method,Manual User::findOrFail($id) in every method,public function show(User $user): View { return view('users.show' compact('user')); },public function show($id) { $user = User::findOrFail($id); },Medium,https://laravel.com/docs/routing#route-model-binding
45,Performance,Enable HTTP response caching for static content,Cache control headers for pages that rarely change,Cache-Control headers via middleware for public pages,No caching — serve every response fresh,response()->view('home')->header('Cache-Control' 'public, max-age=3600'),No cache headers on marketing pages,Medium,https://laravel.com/docs/responses#response-headers
46,Security,Escape all output in Blade,{{ }} auto-escapes HTML — never use {!! !!} on user data,{{ }} for all untrusted or dynamic content,{!! !!} for user-controlled strings,{{ $comment->body }},{!! $comment->body !!},High,https://laravel.com/docs/blade#displaying-data
47,Security,Protect routes with Gate and Policy,Use policies for authorization — never inline permission checks in views,@can / Gate::allows() for UI visibility; policy()->authorize() for actions,Hardcode role checks inline across templates,@can('update' $post) <a href="{{ route('posts.edit' $post) }}">Edit</a> @endcan,@if(auth()->user()->role === 'admin') <a href="/edit">,High,https://laravel.com/docs/authorization#policies
48,Security,Validate and authorize file uploads,Check MIME type size and store outside public root,Store in storage/app/private + validate mimes and max,Store raw upload in public/ without validation,"'avatar' => ['required' 'image' 'mimes:jpg,png' 'max:2048']",'avatar' => 'required' with no MIME or size check,High,https://laravel.com/docs/filesystem#file-uploads
49,Security,Use signed URLs for temporary links,Generate expiring URLs for private downloads or email confirmations,URL::signedRoute() or temporarySignedRoute(),Expose sequential IDs in download URLs without auth,URL::temporarySignedRoute('file.download' now()->addMinutes(30) ['file' => $id]),route('file.download' $id) with no expiry or signature,High,https://laravel.com/docs/urls#signed-urls
50,Security,Set a strict Content Security Policy,CSP headers prevent XSS injection of external scripts,spatie/laravel-csp or custom middleware to emit CSP header,No CSP — browser runs any injected script,Header: Content-Security-Policy: default-src 'self'; script-src 'self',No Content-Security-Policy header on responses,Medium,https://laravel.com/docs/middleware
Can't render this file because it contains an unexpected character in line 2 and column 209.

View File

@@ -0,0 +1,53 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Routing,Use App Router for new projects,App Router is the recommended approach in Next.js 14+,app/ directory with page.tsx,pages/ for new projects,app/dashboard/page.tsx,pages/dashboard.tsx,Medium,https://nextjs.org/docs/app
2,Routing,Use file-based routing,Create routes by adding files in app directory,page.tsx for routes layout.tsx for layouts,Manual route configuration,app/blog/[slug]/page.tsx,Custom router setup,Medium,https://nextjs.org/docs/app/building-your-application/routing
3,Routing,Colocate related files,Keep components styles tests with their routes,Component files alongside page.tsx,Separate components folder,app/dashboard/_components/,components/dashboard/,Low,
4,Routing,Use route groups for organization,Group routes without affecting URL,Parentheses for route groups,Nested folders affecting URL,(marketing)/about/page.tsx,marketing/about/page.tsx,Low,https://nextjs.org/docs/app/building-your-application/routing/route-groups
5,Routing,Handle loading states,Use loading.tsx for route loading UI,loading.tsx alongside page.tsx,Manual loading state management,app/dashboard/loading.tsx,useState for loading in page,Medium,https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
6,Routing,Handle errors with error.tsx,Catch errors at route level,error.tsx with reset function,try/catch in every component,app/dashboard/error.tsx,try/catch in page component,High,https://nextjs.org/docs/app/building-your-application/routing/error-handling
7,Rendering,Use Server Components by default,Server Components reduce client JS bundle,Keep components server by default,Add 'use client' unnecessarily,export default function Page(),('use client') for static content,High,https://nextjs.org/docs/app/building-your-application/rendering/server-components
8,Rendering,Mark Client Components explicitly,'use client' for interactive components,Add 'use client' only when needed,Server Component with hooks/events,('use client') for onClick useState,No directive with useState,High,https://nextjs.org/docs/app/building-your-application/rendering/client-components
9,Rendering,Push Client Components down,Keep Client Components as leaf nodes,Client wrapper for interactive parts only,Mark page as Client Component,<InteractiveButton/> in Server Page,('use client') on page.tsx,High,
10,Rendering,Use streaming for better UX,Stream content with Suspense boundaries,Suspense for slow data fetches,Wait for all data before render,<Suspense><SlowComponent/></Suspense>,await allData then render,Medium,https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
11,Rendering,Choose correct rendering strategy,SSG for static SSR for dynamic ISR for semi-static,generateStaticParams for known paths,SSR for static content,export const revalidate = 3600,fetch without cache config,Medium,
12,DataFetching,Fetch data in Server Components,Fetch directly in async Server Components,async function Page() { const data = await fetch() },useEffect for initial data,const data = await fetch(url),useEffect(() => fetch(url)),High,https://nextjs.org/docs/app/building-your-application/data-fetching
13,DataFetching,Configure caching explicitly (Next.js 15+),Next.js 15 changed defaults to uncached for fetch,Explicitly set cache: 'force-cache' for static data,Assume default is cached (it's not in Next.js 15),fetch(url { cache: 'force-cache' }),fetch(url) // Uncached in v15,High,https://nextjs.org/docs/app/building-your-application/upgrading/version-15
14,DataFetching,Deduplicate fetch requests,React and Next.js dedupe same requests,Same fetch call in multiple components,Manual request deduplication,Multiple components fetch same URL,Custom cache layer,Low,
15,DataFetching,Use Server Actions for mutations,Server Actions for form submissions,action={serverAction} in forms,API route for every mutation,<form action={createPost}>,<form onSubmit={callApiRoute}>,Medium,https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
16,DataFetching,Revalidate data appropriately,Use revalidatePath/revalidateTag after mutations,Revalidate after Server Action,'use client' with manual refetch,revalidatePath('/posts'),router.refresh() everywhere,Medium,https://nextjs.org/docs/app/building-your-application/caching#revalidating
17,Images,Use next/image for optimization,Automatic image optimization and lazy loading,<Image> component for all images,<img> tags directly,<Image src={} alt={} width={} height={}>,<img src={}/>,High,https://nextjs.org/docs/app/building-your-application/optimizing/images
18,Images,Provide width and height,Prevent layout shift with dimensions,width and height props or fill,Missing dimensions,<Image width={400} height={300}/>,<Image src={url}/>,High,
19,Images,Use fill for responsive images,Fill container with object-fit,fill prop with relative parent,Fixed dimensions for responsive,"<Image fill className=""object-cover""/>",<Image width={window.width}/>,Medium,
20,Images,Configure remote image domains,Whitelist external image sources,remotePatterns in next.config.js,Allow all domains,remotePatterns: [{ hostname: 'cdn.example.com' }],domains: ['*'],High,https://nextjs.org/docs/app/api-reference/components/image#remotepatterns
21,Images,Use priority for LCP images,Mark above-fold images as priority,priority prop on hero images,All images with priority,<Image priority src={hero}/>,<Image priority/> on every image,Medium,
22,Fonts,Use next/font for fonts,Self-hosted fonts with zero layout shift,next/font/google or next/font/local,External font links,import { Inter } from 'next/font/google',"<link href=""fonts.googleapis.com""/>",Medium,https://nextjs.org/docs/app/building-your-application/optimizing/fonts
23,Fonts,Apply font to layout,Set font in root layout for consistency,className on body in layout.tsx,Font in individual pages,<body className={inter.className}>,Each page imports font,Low,
24,Fonts,Use variable fonts,Variable fonts reduce bundle size,Single variable font file,Multiple font weights as files,Inter({ subsets: ['latin'] }),Inter_400 Inter_500 Inter_700,Low,
25,Metadata,Use generateMetadata for dynamic,Generate metadata based on params,export async function generateMetadata(),Hardcoded metadata everywhere,generateMetadata({ params }),export const metadata = {},Medium,https://nextjs.org/docs/app/building-your-application/optimizing/metadata
26,Metadata,Include OpenGraph images,Add OG images for social sharing,opengraph-image.tsx or og property,Missing social preview images,opengraph: { images: ['/og.png'] },No OG configuration,Medium,
27,Metadata,Use metadata API,Export metadata object for static metadata,export const metadata = {},Manual head tags,export const metadata = { title: 'Page' },<head><title>Page</title></head>,Medium,
28,API,Use Route Handlers for APIs,app/api routes for API endpoints,app/api/users/route.ts,pages/api for new projects,export async function GET(request),export default function handler,Medium,https://nextjs.org/docs/app/building-your-application/routing/route-handlers
29,API,Return proper Response objects,Use NextResponse for API responses,NextResponse.json() for JSON,Plain objects or res.json(),return NextResponse.json({ data }),return { data },Medium,
30,API,Handle HTTP methods explicitly,Export named functions for methods,Export GET POST PUT DELETE,Single handler for all methods,export async function POST(),switch(req.method),Low,
31,API,Validate request body,Validate input before processing,Zod or similar for validation,Trust client input,const body = schema.parse(await req.json()),const body = await req.json(),High,
32,Middleware,Use middleware for auth,Protect routes with middleware.ts,middleware.ts at root,Auth check in every page,export function middleware(request),if (!session) redirect in page,Medium,https://nextjs.org/docs/app/building-your-application/routing/middleware
33,Middleware,Match specific paths,Configure middleware matcher,config.matcher for specific routes,Run middleware on all routes,matcher: ['/dashboard/:path*'],No matcher config,Medium,
34,Middleware,Keep middleware edge-compatible,Middleware runs on Edge runtime,Edge-compatible code only,Node.js APIs in middleware,Edge-compatible auth check,fs.readFile in middleware,High,
35,Environment,Use NEXT_PUBLIC prefix,Client-accessible env vars need prefix,NEXT_PUBLIC_ for client vars,Server vars exposed to client,NEXT_PUBLIC_API_URL,API_SECRET in client code,High,https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
36,Environment,Validate env vars,Check required env vars exist,Validate on startup,Undefined env at runtime,if (!process.env.DATABASE_URL) throw,process.env.DATABASE_URL (might be undefined),High,
37,Environment,Use .env.local for secrets,Local env file for development secrets,.env.local gitignored,Secrets in .env committed,.env.local with secrets,.env with DATABASE_PASSWORD,High,
38,Performance,Analyze bundle size,Use @next/bundle-analyzer,Bundle analyzer in dev,Ship large bundles blindly,ANALYZE=true npm run build,No bundle analysis,Medium,https://nextjs.org/docs/app/building-your-application/optimizing/bundle-analyzer
39,Performance,Use dynamic imports,Code split with next/dynamic,dynamic() for heavy components,Import everything statically,const Chart = dynamic(() => import('./Chart')),import Chart from './Chart',Medium,https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading
40,Performance,Avoid layout shifts,Reserve space for dynamic content,Skeleton loaders aspect ratios,Content popping in,"<Skeleton className=""h-48""/>",No placeholder for async content,High,
41,Performance,Use Partial Prerendering,Combine static and dynamic in one route,Static shell with Suspense holes,Full dynamic or static pages,Static header + dynamic content,Entire page SSR,Low,https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering
42,Link,Use next/link for navigation,Client-side navigation with prefetching,"<Link href=""""> for internal links",<a> for internal navigation,"<Link href=""/about"">About</Link>","<a href=""/about"">About</a>",High,https://nextjs.org/docs/app/api-reference/components/link
43,Link,Prefetch strategically,Control prefetching behavior,prefetch={false} for low-priority,Prefetch all links,<Link prefetch={false}>,Default prefetch on every link,Low,
44,Link,Use scroll option appropriately,Control scroll behavior on navigation,scroll={false} for tabs pagination,Always scroll to top,<Link scroll={false}>,Manual scroll management,Low,
45,Config,Use next.config.js correctly,Configure Next.js behavior,Proper config options,Deprecated or wrong options,images: { remotePatterns: [] },images: { domains: [] },Medium,https://nextjs.org/docs/app/api-reference/next-config-js
46,Config,Enable strict mode,Catch potential issues early,reactStrictMode: true,Strict mode disabled,reactStrictMode: true,reactStrictMode: false,Medium,
47,Config,Configure redirects and rewrites,Use config for URL management,redirects() rewrites() in config,Manual redirect handling,redirects: async () => [...],res.redirect in pages,Medium,https://nextjs.org/docs/app/api-reference/next-config-js/redirects
48,Deployment,Use Vercel for easiest deploy,Vercel optimized for Next.js,Deploy to Vercel,Self-host without knowledge,vercel deploy,Complex Docker setup for simple app,Low,https://nextjs.org/docs/app/building-your-application/deploying
49,Deployment,Configure output for self-hosting,Set output option for deployment target,output: 'standalone' for Docker,Default output for containers,output: 'standalone',No output config for Docker,Medium,https://nextjs.org/docs/app/building-your-application/deploying#self-hosting
50,Security,Sanitize user input,Never trust user input,Escape sanitize validate all input,Direct interpolation of user data,DOMPurify.sanitize(userInput),dangerouslySetInnerHTML={{ __html: userInput }},High,
51,Security,Use CSP headers,Content Security Policy for XSS protection,Configure CSP in next.config.js,No security headers,headers() with CSP,No CSP configuration,High,https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy
52,Security,Validate Server Action input,Server Actions are public endpoints,Validate and authorize in Server Action,Trust Server Action input,Auth check + validation in action,Direct database call without check,High,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Routing Use App Router for new projects App Router is the recommended approach in Next.js 14+ app/ directory with page.tsx pages/ for new projects app/dashboard/page.tsx pages/dashboard.tsx Medium https://nextjs.org/docs/app
3 2 Routing Use file-based routing Create routes by adding files in app directory page.tsx for routes layout.tsx for layouts Manual route configuration app/blog/[slug]/page.tsx Custom router setup Medium https://nextjs.org/docs/app/building-your-application/routing
4 3 Routing Colocate related files Keep components styles tests with their routes Component files alongside page.tsx Separate components folder app/dashboard/_components/ components/dashboard/ Low
5 4 Routing Use route groups for organization Group routes without affecting URL Parentheses for route groups Nested folders affecting URL (marketing)/about/page.tsx marketing/about/page.tsx Low https://nextjs.org/docs/app/building-your-application/routing/route-groups
6 5 Routing Handle loading states Use loading.tsx for route loading UI loading.tsx alongside page.tsx Manual loading state management app/dashboard/loading.tsx useState for loading in page Medium https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
7 6 Routing Handle errors with error.tsx Catch errors at route level error.tsx with reset function try/catch in every component app/dashboard/error.tsx try/catch in page component High https://nextjs.org/docs/app/building-your-application/routing/error-handling
8 7 Rendering Use Server Components by default Server Components reduce client JS bundle Keep components server by default Add 'use client' unnecessarily export default function Page() ('use client') for static content High https://nextjs.org/docs/app/building-your-application/rendering/server-components
9 8 Rendering Mark Client Components explicitly 'use client' for interactive components Add 'use client' only when needed Server Component with hooks/events ('use client') for onClick useState No directive with useState High https://nextjs.org/docs/app/building-your-application/rendering/client-components
10 9 Rendering Push Client Components down Keep Client Components as leaf nodes Client wrapper for interactive parts only Mark page as Client Component <InteractiveButton/> in Server Page ('use client') on page.tsx High
11 10 Rendering Use streaming for better UX Stream content with Suspense boundaries Suspense for slow data fetches Wait for all data before render <Suspense><SlowComponent/></Suspense> await allData then render Medium https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
12 11 Rendering Choose correct rendering strategy SSG for static SSR for dynamic ISR for semi-static generateStaticParams for known paths SSR for static content export const revalidate = 3600 fetch without cache config Medium
13 12 DataFetching Fetch data in Server Components Fetch directly in async Server Components async function Page() { const data = await fetch() } useEffect for initial data const data = await fetch(url) useEffect(() => fetch(url)) High https://nextjs.org/docs/app/building-your-application/data-fetching
14 13 DataFetching Configure caching explicitly (Next.js 15+) Next.js 15 changed defaults to uncached for fetch Explicitly set cache: 'force-cache' for static data Assume default is cached (it's not in Next.js 15) fetch(url { cache: 'force-cache' }) fetch(url) // Uncached in v15 High https://nextjs.org/docs/app/building-your-application/upgrading/version-15
15 14 DataFetching Deduplicate fetch requests React and Next.js dedupe same requests Same fetch call in multiple components Manual request deduplication Multiple components fetch same URL Custom cache layer Low
16 15 DataFetching Use Server Actions for mutations Server Actions for form submissions action={serverAction} in forms API route for every mutation <form action={createPost}> <form onSubmit={callApiRoute}> Medium https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
17 16 DataFetching Revalidate data appropriately Use revalidatePath/revalidateTag after mutations Revalidate after Server Action 'use client' with manual refetch revalidatePath('/posts') router.refresh() everywhere Medium https://nextjs.org/docs/app/building-your-application/caching#revalidating
18 17 Images Use next/image for optimization Automatic image optimization and lazy loading <Image> component for all images <img> tags directly <Image src={} alt={} width={} height={}> <img src={}/> High https://nextjs.org/docs/app/building-your-application/optimizing/images
19 18 Images Provide width and height Prevent layout shift with dimensions width and height props or fill Missing dimensions <Image width={400} height={300}/> <Image src={url}/> High
20 19 Images Use fill for responsive images Fill container with object-fit fill prop with relative parent Fixed dimensions for responsive <Image fill className="object-cover"/> <Image width={window.width}/> Medium
21 20 Images Configure remote image domains Whitelist external image sources remotePatterns in next.config.js Allow all domains remotePatterns: [{ hostname: 'cdn.example.com' }] domains: ['*'] High https://nextjs.org/docs/app/api-reference/components/image#remotepatterns
22 21 Images Use priority for LCP images Mark above-fold images as priority priority prop on hero images All images with priority <Image priority src={hero}/> <Image priority/> on every image Medium
23 22 Fonts Use next/font for fonts Self-hosted fonts with zero layout shift next/font/google or next/font/local External font links import { Inter } from 'next/font/google' <link href="fonts.googleapis.com"/> Medium https://nextjs.org/docs/app/building-your-application/optimizing/fonts
24 23 Fonts Apply font to layout Set font in root layout for consistency className on body in layout.tsx Font in individual pages <body className={inter.className}> Each page imports font Low
25 24 Fonts Use variable fonts Variable fonts reduce bundle size Single variable font file Multiple font weights as files Inter({ subsets: ['latin'] }) Inter_400 Inter_500 Inter_700 Low
26 25 Metadata Use generateMetadata for dynamic Generate metadata based on params export async function generateMetadata() Hardcoded metadata everywhere generateMetadata({ params }) export const metadata = {} Medium https://nextjs.org/docs/app/building-your-application/optimizing/metadata
27 26 Metadata Include OpenGraph images Add OG images for social sharing opengraph-image.tsx or og property Missing social preview images opengraph: { images: ['/og.png'] } No OG configuration Medium
28 27 Metadata Use metadata API Export metadata object for static metadata export const metadata = {} Manual head tags export const metadata = { title: 'Page' } <head><title>Page</title></head> Medium
29 28 API Use Route Handlers for APIs app/api routes for API endpoints app/api/users/route.ts pages/api for new projects export async function GET(request) export default function handler Medium https://nextjs.org/docs/app/building-your-application/routing/route-handlers
30 29 API Return proper Response objects Use NextResponse for API responses NextResponse.json() for JSON Plain objects or res.json() return NextResponse.json({ data }) return { data } Medium
31 30 API Handle HTTP methods explicitly Export named functions for methods Export GET POST PUT DELETE Single handler for all methods export async function POST() switch(req.method) Low
32 31 API Validate request body Validate input before processing Zod or similar for validation Trust client input const body = schema.parse(await req.json()) const body = await req.json() High
33 32 Middleware Use middleware for auth Protect routes with middleware.ts middleware.ts at root Auth check in every page export function middleware(request) if (!session) redirect in page Medium https://nextjs.org/docs/app/building-your-application/routing/middleware
34 33 Middleware Match specific paths Configure middleware matcher config.matcher for specific routes Run middleware on all routes matcher: ['/dashboard/:path*'] No matcher config Medium
35 34 Middleware Keep middleware edge-compatible Middleware runs on Edge runtime Edge-compatible code only Node.js APIs in middleware Edge-compatible auth check fs.readFile in middleware High
36 35 Environment Use NEXT_PUBLIC prefix Client-accessible env vars need prefix NEXT_PUBLIC_ for client vars Server vars exposed to client NEXT_PUBLIC_API_URL API_SECRET in client code High https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
37 36 Environment Validate env vars Check required env vars exist Validate on startup Undefined env at runtime if (!process.env.DATABASE_URL) throw process.env.DATABASE_URL (might be undefined) High
38 37 Environment Use .env.local for secrets Local env file for development secrets .env.local gitignored Secrets in .env committed .env.local with secrets .env with DATABASE_PASSWORD High
39 38 Performance Analyze bundle size Use @next/bundle-analyzer Bundle analyzer in dev Ship large bundles blindly ANALYZE=true npm run build No bundle analysis Medium https://nextjs.org/docs/app/building-your-application/optimizing/bundle-analyzer
40 39 Performance Use dynamic imports Code split with next/dynamic dynamic() for heavy components Import everything statically const Chart = dynamic(() => import('./Chart')) import Chart from './Chart' Medium https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading
41 40 Performance Avoid layout shifts Reserve space for dynamic content Skeleton loaders aspect ratios Content popping in <Skeleton className="h-48"/> No placeholder for async content High
42 41 Performance Use Partial Prerendering Combine static and dynamic in one route Static shell with Suspense holes Full dynamic or static pages Static header + dynamic content Entire page SSR Low https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering
43 42 Link Use next/link for navigation Client-side navigation with prefetching <Link href=""> for internal links <a> for internal navigation <Link href="/about">About</Link> <a href="/about">About</a> High https://nextjs.org/docs/app/api-reference/components/link
44 43 Link Prefetch strategically Control prefetching behavior prefetch={false} for low-priority Prefetch all links <Link prefetch={false}> Default prefetch on every link Low
45 44 Link Use scroll option appropriately Control scroll behavior on navigation scroll={false} for tabs pagination Always scroll to top <Link scroll={false}> Manual scroll management Low
46 45 Config Use next.config.js correctly Configure Next.js behavior Proper config options Deprecated or wrong options images: { remotePatterns: [] } images: { domains: [] } Medium https://nextjs.org/docs/app/api-reference/next-config-js
47 46 Config Enable strict mode Catch potential issues early reactStrictMode: true Strict mode disabled reactStrictMode: true reactStrictMode: false Medium
48 47 Config Configure redirects and rewrites Use config for URL management redirects() rewrites() in config Manual redirect handling redirects: async () => [...] res.redirect in pages Medium https://nextjs.org/docs/app/api-reference/next-config-js/redirects
49 48 Deployment Use Vercel for easiest deploy Vercel optimized for Next.js Deploy to Vercel Self-host without knowledge vercel deploy Complex Docker setup for simple app Low https://nextjs.org/docs/app/building-your-application/deploying
50 49 Deployment Configure output for self-hosting Set output option for deployment target output: 'standalone' for Docker Default output for containers output: 'standalone' No output config for Docker Medium https://nextjs.org/docs/app/building-your-application/deploying#self-hosting
51 50 Security Sanitize user input Never trust user input Escape sanitize validate all input Direct interpolation of user data DOMPurify.sanitize(userInput) dangerouslySetInnerHTML={{ __html: userInput }} High
52 51 Security Use CSP headers Content Security Policy for XSS protection Configure CSP in next.config.js No security headers headers() with CSP No CSP configuration High https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy
53 52 Security Validate Server Action input Server Actions are public endpoints Validate and authorize in Server Action Trust Server Action input Auth check + validation in action Direct database call without check High

View File

@@ -0,0 +1,71 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Installation,Add Nuxt UI module,Install and configure Nuxt UI in your Nuxt project,pnpm add @nuxt/ui and add to modules,Manual component imports,"modules: ['@nuxt/ui']","import { UButton } from '@nuxt/ui'",High,https://ui.nuxt.com/docs/getting-started/installation/nuxt
2,Installation,Import Tailwind and Nuxt UI CSS,Required CSS imports in main.css file,@import tailwindcss and @import @nuxt/ui,Skip CSS imports,"@import ""tailwindcss""; @import ""@nuxt/ui"";",No CSS imports,High,https://ui.nuxt.com/docs/getting-started/installation/nuxt
3,Installation,Wrap app with UApp component,UApp provides global configs for Toast Tooltip and overlays,<UApp> wrapper in app.vue,Skip UApp wrapper,<UApp><NuxtPage/></UApp>,<NuxtPage/> without wrapper,High,https://ui.nuxt.com/docs/components/app
4,Components,Use U prefix for components,All Nuxt UI components use U prefix by default,UButton UInput UModal,Button Input Modal,<UButton>Click</UButton>,<Button>Click</Button>,Medium,https://ui.nuxt.com/docs/getting-started/installation/nuxt
5,Components,Use semantic color props,Use semantic colors like primary secondary error,color="primary" color="error",Hardcoded colors,"<UButton color=""primary"">","<UButton class=""bg-green-500"">",Medium,https://ui.nuxt.com/docs/getting-started/theme/design-system
6,Components,Use variant prop for styling,Nuxt UI provides solid outline soft subtle ghost link variants,variant="soft" variant="outline",Custom button classes,"<UButton variant=""soft"">","<UButton class=""border bg-transparent"">",Medium,https://ui.nuxt.com/docs/components/button
7,Components,Use size prop consistently,Components support xs sm md lg xl sizes,size="sm" size="lg",Arbitrary sizing classes,"<UButton size=""lg"">","<UButton class=""text-xl px-6"">",Low,https://ui.nuxt.com/docs/components/button
8,Icons,Use i-{collection}-{name} format for icons,Nuxt UI v4 uses Iconify i-prefix format — lucide:home is v3 legacy,i-lucide-home i-heroicons-user format,lucide:home format (v3 syntax),"<UButton icon=""i-lucide-home"">","<UButton icon=""lucide:home"">",High,https://ui.nuxt.com/docs/getting-started/installation/nuxt
9,Icons,Use leadingIcon and trailingIcon props,Position icons with dedicated props for clarity,leadingIcon="i-lucide-plus" trailingIcon="i-lucide-arrow-right",Manual icon positioning or slots,"<UButton leadingIcon=""i-lucide-plus"" label=""Add"">","<UButton><UIcon name=""i-lucide-plus""/>Add</UButton>",Low,https://ui.nuxt.com/docs/components/button
10,Theming,Configure colors in app.config.ts,Runtime color configuration without restart,ui.colors.primary in app.config.ts,Hardcoded colors in components,"defineAppConfig({ ui: { colors: { primary: 'blue' } } })","<UButton class=""bg-blue-500"">",High,https://ui.nuxt.com/docs/getting-started/theme/design-system
11,Theming,Use @theme directive for custom colors,Define design tokens in CSS with Tailwind @theme,@theme { --color-brand-500: #xxx },Inline color definitions,@theme { --color-brand-500: #ef4444; },:style="{ color: '#ef4444' }",Medium,https://ui.nuxt.com/docs/getting-started/theme/design-system
12,Theming,Extend semantic colors in nuxt.config,Register new colors like tertiary in theme.colors,theme.colors array in ui config,Use undefined colors,"ui: { theme: { colors: ['primary', 'tertiary'] } }","<UButton color=""tertiary""> without config",Medium,https://ui.nuxt.com/docs/getting-started/theme/design-system
13,Forms,Use UForm with schema validation,UForm supports Zod Yup Joi Valibot schemas,:schema prop with validation schema,Manual form validation,"<UForm :schema=""schema"" :state=""state"">",Manual @blur validation,High,https://ui.nuxt.com/docs/components/form
14,Forms,Use UFormField for field wrapper,Provides label error message and validation display,UFormField with name prop,Manual error handling,"<UFormField name=""email"" label=""Email"">",<div><label>Email</label><UInput/><span>error</span></div>,Medium,https://ui.nuxt.com/docs/components/form-field
15,Forms,Handle form submit with @submit,UForm emits submit event with validated data,@submit handler on UForm,@click on submit button,"<UForm @submit=""onSubmit"">","<UButton @click=""onSubmit"">",Medium,https://ui.nuxt.com/docs/components/form
16,Forms,Use validateOn prop for validation timing,Control when validation triggers (blur change input),validateOn="['blur']" for performance,Always validate on input,"<UForm :validateOn=""['blur', 'change']"">","<UForm> (validates on every keystroke)",Low,https://ui.nuxt.com/docs/components/form
17,Overlays,Use v-model:open for overlay control,Modal Slideover Drawer use v-model:open,v-model:open for controlled state,Manual show/hide logic,"<UModal v-model:open=""isOpen"">",<UModal v-if="isOpen">,Medium,https://ui.nuxt.com/docs/components/modal
18,Overlays,Use useOverlay composable for programmatic overlays,Open overlays programmatically — v4 API is create().open() not open(Component),overlay.create(Component).open({ props }) pattern,v3 overlay.open(Component) pattern (removed in v4),"const modal = overlay.create(MyModal); const { result } = modal.open({ title: 'Confirm' })","overlay.open(MyModal, { props: { title: 'Confirm' } })",High,https://ui.nuxt.com/docs/components/modal
19,Overlays,Use title and description props,Built-in header support for overlays,title="Confirm" description="Are you sure?",Manual header content,"<UModal title=""Confirm"" description=""Are you sure?"">","<UModal><template #header><h2>Confirm</h2></template>",Low,https://ui.nuxt.com/docs/components/modal
20,Dashboard,Use UDashboardSidebar for navigation,Provides collapsible resizable sidebar with mobile support,UDashboardSidebar with header default footer slots,Custom sidebar implementation,<UDashboardSidebar><template #header>...</template></UDashboardSidebar>,<aside class="w-64 border-r">,Medium,https://ui.nuxt.com/docs/components/dashboard-sidebar
21,Dashboard,Use UDashboardGroup for layout,Wraps dashboard components with sidebar state management,UDashboardGroup > UDashboardSidebar + UDashboardPanel,Manual layout flex containers,<UDashboardGroup><UDashboardSidebar/><UDashboardPanel/></UDashboardGroup>,"<div class=""flex""><aside/><main/></div>",Medium,https://ui.nuxt.com/docs/components/dashboard-group
22,Dashboard,Use UDashboardNavbar for top navigation,Responsive navbar with mobile menu support,UDashboardNavbar in dashboard layout,Custom navbar implementation,<UDashboardNavbar :links="navLinks"/>,<nav class="border-b">,Low,https://ui.nuxt.com/docs/components/dashboard-navbar
23,Tables,Use UTable with data and columns props,Powered by TanStack Table with built-in features,:data and :columns props,Manual table markup,"<UTable :data=""users"" :columns=""columns""/>","<table><tr v-for=""user in users"">",High,https://ui.nuxt.com/docs/components/table
24,Tables,Define columns with accessorKey,Column definitions use accessorKey for data binding,accessorKey: 'email' in column def,String column names only,"{ accessorKey: 'email', header: 'Email' }","['name', 'email']",Medium,https://ui.nuxt.com/docs/components/table
25,Tables,Use cell slot for custom rendering,Customize cell content with scoped slots,#cell-columnName slot,Override entire table,<template #cell-status="{ row }">,Manual column render function,Medium,https://ui.nuxt.com/docs/components/table
26,Tables,Enable sorting with sortable column option,Add sortable: true to column definition,sortable: true in column,Manual sort implementation,"{ accessorKey: 'name', sortable: true }",@click="sortBy('name')",Low,https://ui.nuxt.com/docs/components/table
27,Navigation,Use UNavigationMenu for nav links,Horizontal or vertical navigation with dropdown support,UNavigationMenu with items array,Manual nav with v-for,"<UNavigationMenu :items=""navItems""/>","<nav><a v-for=""item in items"">",Medium,https://ui.nuxt.com/docs/components/navigation-menu
28,Navigation,Use UBreadcrumb for page hierarchy,Automatic breadcrumb with NuxtLink support,:items array with label and to,Manual breadcrumb links,"<UBreadcrumb :items=""breadcrumbs""/>","<nav><span v-for=""crumb in crumbs"">",Low,https://ui.nuxt.com/docs/components/breadcrumb
29,Navigation,Use UTabs for tabbed content,Tab navigation with content panels,UTabs with items containing slot content,Manual tab state,"<UTabs :items=""tabs""/>","<div><button @click=""tab=1"">",Medium,https://ui.nuxt.com/docs/components/tabs
30,Feedback,Use useToast for notifications,Composable for toast notifications,useToast().add({ title description }),Alert components for toasts,"const toast = useToast(); toast.add({ title: 'Saved' })",<UAlert v-if="showSuccess">,High,https://ui.nuxt.com/docs/components/toast
31,Feedback,Use UAlert for inline messages,Static alert messages with icon and actions,UAlert with title description color,Toast for static messages,"<UAlert title=""Warning"" color=""warning""/>",useToast for inline alerts,Medium,https://ui.nuxt.com/docs/components/alert
32,Feedback,Use USkeleton for loading states,Placeholder content during data loading,USkeleton with appropriate size,Spinner for content loading,<USkeleton class="h-4 w-32"/>,<UIcon name="lucide:loader" class="animate-spin"/>,Low,https://ui.nuxt.com/docs/components/skeleton
33,Color Mode,Use UColorModeButton for theme toggle,Built-in light/dark mode toggle button,UColorModeButton component,Manual color mode logic,<UColorModeButton/>,"<button @click=""toggleColorMode"">",Low,https://ui.nuxt.com/docs/components/color-mode-button
34,Color Mode,Use UColorModeSelect for theme picker,Dropdown to select system light or dark mode,UColorModeSelect component,Custom select for theme,<UColorModeSelect/>,"<USelect v-model=""colorMode"" :items=""modes""/>",Low,https://ui.nuxt.com/docs/components/color-mode-select
35,Customization,Use ui prop for component styling,Override component styles via ui prop,ui prop with slot class overrides,Global CSS overrides,"<UButton :ui=""{ base: 'rounded-full' }""/>",<UButton class="!rounded-full"/>,Medium,https://ui.nuxt.com/docs/getting-started/theme/components
36,Customization,Configure default variants in nuxt.config,Set default color and size for all components,theme.defaultVariants in ui config,Repeat props on every component,"ui: { theme: { defaultVariants: { color: 'neutral' } } }","<UButton color=""neutral""> everywhere",Medium,https://ui.nuxt.com/docs/getting-started/installation/nuxt
37,Customization,Use app.config.ts for theme overrides,Runtime theme customization,defineAppConfig with ui key,nuxt.config for runtime values,"defineAppConfig({ ui: { button: { defaultVariants: { size: 'sm' } } } })","nuxt.config ui.button.size: 'sm'",Medium,https://ui.nuxt.com/docs/getting-started/theme/components
38,Performance,Enable component detection,Tree-shake unused component CSS,experimental.componentDetection: true,Include all component CSS,"ui: { experimental: { componentDetection: true } }","ui: {} (includes all CSS)",Low,https://ui.nuxt.com/docs/getting-started/installation/nuxt
39,Performance,Use UTable virtualize for large data,Enable virtualization for 1000+ rows,:virtualize prop on UTable,Render all rows,"<UTable :data=""largeData"" virtualize/>","<UTable :data=""largeData""/>",Medium,https://ui.nuxt.com/docs/components/table
40,Accessibility,Use semantic component props,Components have built-in ARIA support,Use title description label props,Skip accessibility props,"<UModal title=""Settings"">","<UModal><h2>Settings</h2>",Medium,https://ui.nuxt.com/docs/components/modal
41,Accessibility,Use UFormField for form accessibility,Automatic label-input association,UFormField wraps inputs,Manual id and for attributes,"<UFormField label=""Email""><UInput/></UFormField>","<label for=""email"">Email</label><UInput id=""email""/>",High,https://ui.nuxt.com/docs/components/form-field
42,Content,Use UContentToc for table of contents,Automatic TOC with active heading highlight,UContentToc with :links,Manual TOC implementation,"<UContentToc :links=""toc""/>","<nav><a v-for=""heading in headings"">",Low,https://ui.nuxt.com/docs/components/content-toc
43,Content,Use UContentSearch for docs search,Command palette for documentation search,UContentSearch with Nuxt Content,Custom search implementation,<UContentSearch/>,<UCommandPalette :groups="searchResults"/>,Low,https://ui.nuxt.com/docs/components/content-search
44,AI/Chat,Use UChatMessages for chat UI,Designed for Vercel AI SDK integration,UChatMessages with messages array,Custom chat message list,"<UChatMessages :messages=""messages""/>","<div v-for=""msg in messages"">",Medium,https://ui.nuxt.com/docs/components/chat-messages
45,AI/Chat,Use UChatPrompt for input,Enhanced textarea for AI prompts,UChatPrompt with v-model,Basic textarea,<UChatPrompt v-model="prompt"/>,<UTextarea v-model="prompt"/>,Medium,https://ui.nuxt.com/docs/components/chat-prompt
46,Editor,Use UEditor for rich text,TipTap-based editor with toolbar support,UEditor with v-model:content,Custom TipTap setup,"<UEditor v-model:content=""content""/>",Manual TipTap initialization,Medium,https://ui.nuxt.com/docs/components/editor
47,Links,Use to prop for navigation,UButton and ULink support NuxtLink to prop,to="/dashboard" for internal links,href for internal navigation,"<UButton to=""/dashboard"">","<UButton href=""/dashboard"">",Medium,https://ui.nuxt.com/docs/components/button
48,Links,Use external prop for outside links,Explicitly mark external links,target="_blank" with external URLs,Forget rel="noopener","<UButton to=""https://example.com"" target=""_blank"">","<UButton href=""https://..."">",Low,https://ui.nuxt.com/docs/components/link
49,Loading,Use loadingAuto on buttons,Automatic loading state from @click promise,loadingAuto prop on UButton,Manual loading state,"<UButton loadingAuto @click=""async () => await save()"">","<UButton :loading=""isLoading"" @click=""save"">",Low,https://ui.nuxt.com/docs/components/button
50,Loading,Use UForm loadingAuto,Auto-disable form during submit,loadingAuto on UForm (default true),Manual form disabled state,"<UForm @submit=""handleSubmit"">","<UForm :disabled=""isSubmitting"">",Low,https://ui.nuxt.com/docs/components/form
51,Installation,Do not manually add auto-registered modules,Nuxt UI v4 auto-registers @nuxt/icon @nuxt/fonts @nuxtjs/color-mode,Configure via root-level keys in nuxt.config,Adding them to modules array causes duplicate registration,"icon: { /* opts */ } in nuxt.config","modules: ['@nuxt/ui', '@nuxt/icon']",High,https://ui.nuxt.com/docs/getting-started/installation/nuxt
52,Installation,Use official templates to bootstrap projects,Nuxt UI provides starter templates via nuxi init,npx nuxi init -t ui/dashboard for dashboard project,Manual project setup from scratch,"npx nuxi@latest init -t ui/dashboard","pnpm create nuxt app (manual UI setup)",Medium,https://ui.nuxt.com/docs/getting-started/installation/nuxt
53,Icons,Install icon collections locally for SSR,Local Iconify JSON prevents network requests and flash,pnpm i @iconify-json/lucide for reliable server rendering,Rely on remote icon fetching in production,"pnpm i @iconify-json/lucide @iconify-json/simple-icons",No local icon packages,Medium,https://ui.nuxt.com/docs/getting-started/installation/nuxt
54,Icons,Override default component icons globally,Components use default icons configurable via appConfig.ui.icons,Set loading close check icons in app.config.ts,Accept default icons for all components,"defineAppConfig({ ui: { icons: { loading: 'i-lucide-refresh-cw', close: 'i-lucide-x' } } })","<UModal :close-icon=""'i-lucide-x'""> on every usage",Low,https://ui.nuxt.com/docs/getting-started/installation/nuxt
55,Forms,Use UFileUpload for file input,Built-in drag-drop and preview support,UFileUpload with v-model and accept prop,Custom input type=file,"<UFileUpload v-model=""files"" accept=""image/*"" multiple/>","<input type=""file"" @change=""handleFiles"">",Medium,https://ui.nuxt.com/docs/components/file-upload
56,Forms,Use UInputDate for date selection,Locale-aware date picker built on UCalendar,UInputDate with v-model and locale prop,Third-party date picker libraries,"<UInputDate v-model=""date"" />","<DatePicker v-model=""date"" />",Medium,https://ui.nuxt.com/docs/components/input-date
57,Forms,Use UInputTags for tag input,Multi-value tag input with keyboard support,UInputTags with v-model and max prop,Custom chip input implementation,"<UInputTags v-model=""tags"" :max=""5"" />","<UInput @keydown.enter=""addTag"">",Low,https://ui.nuxt.com/docs/components/input-tags
58,Forms,Use UColorPicker for color selection,Full-featured color picker with multiple format support,UColorPicker with v-model and format prop,Native input type=color,"<UColorPicker v-model=""color"" format=""hex"" />","<input type=""color"" v-model=""color"">",Low,https://ui.nuxt.com/docs/components/color-picker
59,Data,Use UTree for hierarchical data,Built-in tree component with expand/collapse,UTree with items prop containing nested children,Custom recursive component,"<UTree :items=""treeItems"" />","<TreeNode v-for=""item in items"" :key=""item.id"">",Low,https://ui.nuxt.com/docs/components/tree
60,Data,Use UMarquee for infinite scroll content,Animated infinite scroll band for logos or testimonials,UMarquee with repeat and pauseOnHover props,CSS animation keyframes loop,"<UMarquee :repeat=""3"" pause-on-hover>","<div class=""animate-marquee"">",Low,https://ui.nuxt.com/docs/components/marquee
61,Overlays,Use UContextMenu for right-click menus,Context menu triggered by right-click on children,UContextMenu wrapping target element,Browser default context menu,<UContextMenu :items="menuItems"><div>Right-click me</div></UContextMenu>,"<div @contextmenu.prevent=""showMenu"">",Medium,https://ui.nuxt.com/docs/components/context-menu
62,Overlays,Await overlay result for confirmation dialogs,useOverlay returns a result Promise resolving to user action,await instance.result to get confirm/cancel,Emit events from overlay components,"const { result } = modal.open(); if (await result) { deleteItem() }","overlay.open(Confirm, { onConfirm: deleteItem })",Medium,https://ui.nuxt.com/docs/components/modal
63,Navigation,Use UCommandPalette with grouped items,Command palette supports grouped search with icons and kbds,groups array with id label items,Flat list without categories,"<UCommandPalette :groups=""[{ id: 'actions', label: 'Actions', items }]""/>","<UCommandPalette :items=""flatList""/>",Medium,https://ui.nuxt.com/docs/components/command-palette
64,Navigation,Use defineShortcuts with extractShortcuts,Wire keyboard shortcuts from menu item kbds automatically,extractShortcuts(items) + defineShortcuts to sync keybindings,Manually duplicate shortcuts from menu items,"defineShortcuts(extractShortcuts(items))","defineShortcuts({ meta_n: () => newFile() }) // duplicated from items",Low,https://ui.nuxt.com/docs/composables/define-shortcuts
65,Layout,Use UHeader and UFooter for page layout,Responsive header/footer with built-in mobile menu,UHeader with #default slot for nav UFooter with columns,Custom header/footer from scratch,"<UHeader><template #right><UNavigationMenu/></template></UHeader>","<header class=""sticky top-0"">",Low,https://ui.nuxt.com/docs/components/header
66,Layout,Use UPageAside for sidebar content,Sidebar that hides below lg breakpoint automatically,UPageAside for docs and landing page sidebars,Manual hidden lg: classes,"<UPageAside><UNavigationMenu orientation=""vertical""/></UPageAside>","<aside class=""hidden lg:block"">",Low,https://ui.nuxt.com/docs/components/page-aside
67,Color Mode,Wrap custom color mode toggles in ClientOnly,Prevents hydration mismatch on server-rendered color mode,ClientOnly with fallback placeholder,Direct useColorMode in template without ClientOnly,"<ClientOnly><USwitch v-model=""isDark""/><template #fallback><div class=""size-8""/></template></ClientOnly>",<USwitch v-model="isDark"/> directly in template,Medium,https://ui.nuxt.com/docs/getting-started/installation/nuxt
68,Theming,Read generated theme file to find slot names,Nuxt UI generates theme files listing all component slots and variants,Check .nuxt/ui/<component>.ts for slot names,Guess slot names or use trial-and-error,".nuxt/ui/button.ts for UButton slot names","<UButton :ui=""{ base: 'rounded-full' }""/> without checking slots",Medium,https://ui.nuxt.com/docs/getting-started/theme/components
69,Composables,Use defineShortcuts whenever keyword shortcut,whenever array condition prevents shortcut firing when inactive,whenever: [isFormValid] to guard shortcut execution,Always-on shortcuts that fire in wrong context,"defineShortcuts({ meta_enter: { handler: submit, whenever: [isFormValid] } })","defineShortcuts({ meta_enter: () => submit() }) // fires even when invalid",Low,https://ui.nuxt.com/docs/composables/define-shortcuts
70,i18n,Use UApp locale prop for internationalization,Nuxt UI supports 50+ built-in locales via locale prop on UApp,Import locale from @nuxt/ui/locale and pass to UApp,Manual translation of component UI strings,"import { fr } from '@nuxt/ui/locale'; // <UApp :locale=""fr"">","<UModal title=""Fermer""> manually for each component",Low,https://ui.nuxt.com/docs/composables/define-locale
Can't render this file because it contains an unexpected character in line 6 and column 94.

View File

@@ -0,0 +1,59 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Routing,Use file-based routing,Create routes by adding files in pages directory,pages/ directory with index.vue,Manual route configuration,pages/dashboard/index.vue,Custom router setup,Medium,https://nuxt.com/docs/getting-started/routing
2,Routing,Use dynamic route parameters,Create dynamic routes with bracket syntax,[id].vue for dynamic params,Hardcoded routes for dynamic content,pages/posts/[id].vue,pages/posts/post1.vue,Medium,https://nuxt.com/docs/getting-started/routing
3,Routing,Use catch-all routes,Handle multiple path segments with [...slug],[...slug].vue for catch-all,Multiple nested dynamic routes,pages/[...slug].vue,pages/[a]/[b]/[c].vue,Low,https://nuxt.com/docs/getting-started/routing
4,Routing,Define page metadata with definePageMeta,Set page-level configuration and middleware,definePageMeta for layout middleware title,Manual route meta configuration,"definePageMeta({ layout: 'admin', middleware: 'auth' })",router.beforeEach for page config,High,https://nuxt.com/docs/api/utils/define-page-meta
5,Routing,Use validate for route params,Validate dynamic route parameters before rendering,validate function in definePageMeta,Manual validation in setup,"definePageMeta({ validate: (route) => /^\d+$/.test(route.params.id) })",if (!valid) navigateTo('/404'),Medium,https://nuxt.com/docs/api/utils/define-page-meta
6,Rendering,Use SSR by default,Server-side rendering is enabled by default,Keep ssr: true (default),Disable SSR unnecessarily,ssr: true (default),ssr: false for all pages,High,https://nuxt.com/docs/guide/concepts/rendering
7,Rendering,Use .client suffix for client-only components,Mark components to render only on client,ComponentName.client.vue suffix,v-if with process.client check,Comments.client.vue,<div v-if="process.client"><Comments/></div>,Medium,https://nuxt.com/docs/guide/directory-structure/components
8,Rendering,Use .server suffix for server-only components,Mark components to render only on server,ComponentName.server.vue suffix,Manual server check,HeavyMarkdown.server.vue,v-if="process.server",Low,https://nuxt.com/docs/guide/directory-structure/components
9,DataFetching,Use useFetch for simple data fetching,Wrapper around useAsyncData for URL fetching,useFetch for API calls,$fetch in onMounted,"const { data } = await useFetch('/api/posts')","onMounted(async () => { data.value = await $fetch('/api/posts') })",High,https://nuxt.com/docs/api/composables/use-fetch
10,DataFetching,Use useAsyncData for complex fetching,Fine-grained control over async data,useAsyncData for CMS or custom fetching,useFetch for non-URL data sources,"const { data } = await useAsyncData('posts', () => cms.getPosts())","const { data } = await useFetch(() => cms.getPosts())",Medium,https://nuxt.com/docs/api/composables/use-async-data
11,DataFetching,Use $fetch for non-reactive requests,$fetch for event handlers and non-component code,$fetch in event handlers or server routes,useFetch in click handlers,"async function submit() { await $fetch('/api/submit', { method: 'POST' }) }","async function submit() { await useFetch('/api/submit') }",High,https://nuxt.com/docs/api/utils/dollarfetch
12,DataFetching,Use lazy option for non-blocking fetch,Defer data fetching for better initial load,lazy: true for below-fold content,Blocking fetch for non-critical data,"useFetch('/api/comments', { lazy: true })",await useFetch('/api/comments') for footer,Medium,https://nuxt.com/docs/api/composables/use-fetch
13,DataFetching,Use server option to control fetch location,Choose where data is fetched,server: false for client-only data,Server fetch for user-specific client data,"useFetch('/api/user-preferences', { server: false })",useFetch for localStorage-dependent data,Medium,https://nuxt.com/docs/api/composables/use-fetch
14,DataFetching,Use pick to reduce payload size,Select only needed fields from response,pick option for large responses,Fetching entire objects when few fields needed,"useFetch('/api/user', { pick: ['id', 'name'] })",useFetch('/api/user') then destructure,Low,https://nuxt.com/docs/api/composables/use-fetch
15,DataFetching,Use transform for data manipulation,Transform data before storing in state,transform option for data shaping,Manual transformation after fetch,"useFetch('/api/posts', { transform: (posts) => posts.map(p => p.title) })",const titles = data.value.map(p => p.title),Low,https://nuxt.com/docs/api/composables/use-fetch
16,DataFetching,Handle loading and error states,Always handle pending and error states,Check status pending error refs,Ignoring loading states,"<div v-if=""status === 'pending'"">Loading...</div>",No loading indicator,High,https://nuxt.com/docs/getting-started/data-fetching
17,Lifecycle,Avoid side effects in script setup root,Move side effects to lifecycle hooks,Side effects in onMounted,setInterval in root script setup,"onMounted(() => { interval = setInterval(...) })","<script setup>setInterval(...)</script>",High,https://nuxt.com/docs/guide/concepts/nuxt-lifecycle
18,Lifecycle,Use onMounted for DOM access,Access DOM only after component is mounted,onMounted for DOM manipulation,Direct DOM access in setup,"onMounted(() => { document.getElementById('el') })","<script setup>document.getElementById('el')</script>",High,https://nuxt.com/docs/api/composables/on-mounted
19,Lifecycle,Use nextTick for post-render access,Wait for DOM updates before accessing elements,await nextTick() after state changes,Immediate DOM access after state change,"count.value++; await nextTick(); el.value.focus()","count.value++; el.value.focus()",Medium,https://nuxt.com/docs/api/utils/next-tick
20,Lifecycle,Use onPrehydrate for pre-hydration logic,Run code before Nuxt hydrates the page,onPrehydrate for client setup,onMounted for hydration-critical code,"onPrehydrate(() => { console.log(window) })",onMounted for pre-hydration needs,Low,https://nuxt.com/docs/api/composables/on-prehydrate
21,Server,Use server/api for API routes,Create API endpoints in server/api directory,server/api/users.ts for /api/users,Manual Express setup,server/api/hello.ts -> /api/hello,app.get('/api/hello'),High,https://nuxt.com/docs/guide/directory-structure/server
22,Server,Use defineEventHandler for handlers,Define server route handlers,defineEventHandler for all handlers,export default function,"export default defineEventHandler((event) => { return { hello: 'world' } })","export default function(req, res) {}",High,https://nuxt.com/docs/guide/directory-structure/server
23,Server,Use server/routes for non-api routes,Routes without /api prefix,server/routes for custom paths,server/api for non-api routes,server/routes/sitemap.xml.ts,server/api/sitemap.xml.ts,Medium,https://nuxt.com/docs/guide/directory-structure/server
24,Server,Use getQuery and readBody for input,Access query params and request body,getQuery(event) readBody(event),Direct event access,"const { id } = getQuery(event)",event.node.req.query,Medium,https://nuxt.com/docs/guide/directory-structure/server
25,Server,Validate server input,Always validate input in server handlers,Zod or similar for validation,Trust client input,"const body = await readBody(event); schema.parse(body)",const body = await readBody(event),High,https://nuxt.com/docs/guide/directory-structure/server
26,State,Use useState for shared reactive state,SSR-friendly shared state across components,useState for cross-component state,ref for shared state,"const count = useState('count', () => 0)",const count = ref(0) in composable,High,https://nuxt.com/docs/api/composables/use-state
27,State,Use unique keys for useState,Prevent state conflicts with unique keys,Descriptive unique keys for each state,Generic or duplicate keys,"useState('user-preferences', () => ({}))",useState('data') in multiple places,Medium,https://nuxt.com/docs/api/composables/use-state
28,State,Use Pinia for complex state,Pinia for advanced state management,@pinia/nuxt for complex apps,Custom state management,useMainStore() with Pinia,Custom reactive store implementation,Medium,https://nuxt.com/docs/getting-started/state-management
29,State,Use callOnce for one-time async operations,Ensure async operations run only once,callOnce for store initialization,Direct await in component,"await callOnce(store.fetch)",await store.fetch() on every render,Medium,https://nuxt.com/docs/api/utils/call-once
30,SEO,Use useSeoMeta for SEO tags,Type-safe SEO meta tag management,useSeoMeta for meta tags,useHead for simple meta,"useSeoMeta({ title: 'Home', ogTitle: 'Home', description: '...' })","useHead({ meta: [{ name: 'description', content: '...' }] })",High,https://nuxt.com/docs/api/composables/use-seo-meta
31,SEO,Use reactive values in useSeoMeta,Dynamic SEO tags with refs or getters,Computed getters for dynamic values,Static values for dynamic content,"useSeoMeta({ title: () => post.value.title })","useSeoMeta({ title: post.value.title })",Medium,https://nuxt.com/docs/api/composables/use-seo-meta
32,SEO,Use useHead for non-meta head elements,Scripts styles links in head,useHead for scripts and links,useSeoMeta for scripts,"useHead({ script: [{ src: '/analytics.js' }] })","useSeoMeta({ script: '...' })",Medium,https://nuxt.com/docs/api/composables/use-head
33,SEO,Include OpenGraph tags,Add OG tags for social sharing,ogTitle ogDescription ogImage,Missing social preview,"useSeoMeta({ ogImage: '/og.png', twitterCard: 'summary_large_image' })",No OG configuration,Medium,https://nuxt.com/docs/api/composables/use-seo-meta
34,Middleware,Use defineNuxtRouteMiddleware,Define route middleware properly,defineNuxtRouteMiddleware wrapper,export default function,"export default defineNuxtRouteMiddleware((to, from) => {})","export default function(to, from) {}",High,https://nuxt.com/docs/guide/directory-structure/middleware
35,Middleware,Use navigateTo for redirects,Redirect in middleware with navigateTo,return navigateTo('/login'),router.push in middleware,"if (!auth) return navigateTo('/login')","if (!auth) router.push('/login')",High,https://nuxt.com/docs/api/utils/navigate-to
36,Middleware,Reference middleware in definePageMeta,Apply middleware to specific pages,middleware array in definePageMeta,Global middleware for page-specific,definePageMeta({ middleware: ['auth'] }),Global auth check for one page,Medium,https://nuxt.com/docs/guide/directory-structure/middleware
37,Middleware,Use .global suffix for global middleware,Apply middleware to all routes,auth.global.ts for app-wide auth,Manual middleware on every page,middleware/auth.global.ts,middleware: ['auth'] on every page,Medium,https://nuxt.com/docs/guide/directory-structure/middleware
38,ErrorHandling,Use createError for errors,Create errors with proper status codes,createError with statusCode,throw new Error,"throw createError({ statusCode: 404, statusMessage: 'Not Found' })",throw new Error('Not Found'),High,https://nuxt.com/docs/api/utils/create-error
39,ErrorHandling,Use NuxtErrorBoundary for local errors,Handle errors within component subtree,NuxtErrorBoundary for component errors,Global error page for local errors,"<NuxtErrorBoundary @error=""log""><template #error=""{ error }"">",error.vue for component errors,Medium,https://nuxt.com/docs/getting-started/error-handling
40,ErrorHandling,Use clearError to recover from errors,Clear error state and optionally redirect,clearError({ redirect: '/' }),Manual error state reset,clearError({ redirect: '/home' }),error.value = null,Medium,https://nuxt.com/docs/api/utils/clear-error
41,ErrorHandling,Use short statusMessage,Keep statusMessage brief for security,Short generic messages,Detailed error info in statusMessage,"createError({ statusCode: 400, statusMessage: 'Bad Request' })","createError({ statusMessage: 'Invalid user ID: 123' })",High,https://nuxt.com/docs/getting-started/error-handling
42,Link,Use NuxtLink for internal navigation,Client-side navigation with prefetching,<NuxtLink to> for internal links,<a href> for internal links,<NuxtLink to="/about">About</NuxtLink>,<a href="/about">About</a>,High,https://nuxt.com/docs/api/components/nuxt-link
43,Link,Configure prefetch behavior,Control when prefetching occurs,prefetchOn for interaction-based,Default prefetch for low-priority,"<NuxtLink prefetch-on=""interaction"">",Always default prefetch,Low,https://nuxt.com/docs/api/components/nuxt-link
44,Link,Use useRouter for programmatic navigation,Navigate programmatically,useRouter().push() for navigation,Direct window.location,"const router = useRouter(); router.push('/dashboard')",window.location.href = '/dashboard',Medium,https://nuxt.com/docs/api/composables/use-router
45,Link,Use navigateTo in composables,Navigate outside components,navigateTo() in middleware or plugins,useRouter in non-component code,return navigateTo('/login'),router.push in middleware,Medium,https://nuxt.com/docs/api/utils/navigate-to
46,AutoImports,Leverage auto-imports,Use auto-imported composables directly,Direct use of ref computed useFetch,Manual imports for Nuxt composables,"const count = ref(0)","import { ref } from 'vue'; const count = ref(0)",Medium,https://nuxt.com/docs/guide/concepts/auto-imports
47,AutoImports,Use #imports for explicit imports,Explicit imports when needed,#imports for clarity or disabled auto-imports,"import from 'vue' when auto-import enabled","import { ref } from '#imports'","import { ref } from 'vue'",Low,https://nuxt.com/docs/guide/concepts/auto-imports
48,AutoImports,Configure third-party auto-imports,Add external package auto-imports,imports.presets in nuxt.config,Manual imports everywhere,"imports: { presets: [{ from: 'vue-i18n', imports: ['useI18n'] }] }",import { useI18n } everywhere,Low,https://nuxt.com/docs/guide/concepts/auto-imports
49,Plugins,Use defineNuxtPlugin,Define plugins properly,defineNuxtPlugin wrapper,export default function,"export default defineNuxtPlugin((nuxtApp) => {})","export default function(ctx) {}",High,https://nuxt.com/docs/guide/directory-structure/plugins
50,Plugins,Use provide for injection,Provide helpers across app,return { provide: {} } for type safety,nuxtApp.provide without types,"return { provide: { hello: (name) => `Hello ${name}!` } }","nuxtApp.provide('hello', fn)",Medium,https://nuxt.com/docs/guide/directory-structure/plugins
51,Plugins,Use .client or .server suffix,Control plugin execution environment,plugin.client.ts for client-only,if (process.client) checks,analytics.client.ts,"if (process.client) { // analytics }",Medium,https://nuxt.com/docs/guide/directory-structure/plugins
52,Environment,Use runtimeConfig for env vars,Access environment variables safely,runtimeConfig in nuxt.config,process.env directly,"runtimeConfig: { apiSecret: '', public: { apiBase: '' } }",process.env.API_SECRET in components,High,https://nuxt.com/docs/guide/going-further/runtime-config
53,Environment,Use NUXT_ prefix for env override,Override config with environment variables,NUXT_API_SECRET NUXT_PUBLIC_API_BASE,Custom env var names,NUXT_PUBLIC_API_BASE=https://api.example.com,API_BASE=https://api.example.com,High,https://nuxt.com/docs/guide/going-further/runtime-config
54,Environment,Access public config with useRuntimeConfig,Get public config in components,useRuntimeConfig().public,Direct process.env access,const config = useRuntimeConfig(); config.public.apiBase,process.env.NUXT_PUBLIC_API_BASE,High,https://nuxt.com/docs/api/composables/use-runtime-config
55,Environment,Keep secrets in private config,Server-only secrets in runtimeConfig root,runtimeConfig.apiSecret (server only),Secrets in public config,runtimeConfig: { dbPassword: '' },runtimeConfig: { public: { dbPassword: '' } },High,https://nuxt.com/docs/guide/going-further/runtime-config
56,Performance,Use Lazy prefix for code splitting,Lazy load components with Lazy prefix,<LazyComponent> for below-fold,Eager load all components,<LazyMountainsList v-if="show"/>,<MountainsList/> for hidden content,Medium,https://nuxt.com/docs/guide/directory-structure/components
57,Performance,Use useLazyFetch for non-blocking data,Alias for useFetch with lazy: true,useLazyFetch for secondary data,useFetch for all requests,"const { data } = useLazyFetch('/api/comments')",await useFetch for comments section,Medium,https://nuxt.com/docs/api/composables/use-lazy-fetch
58,Performance,Use lazy hydration for interactivity,Delay component hydration until needed,LazyComponent with hydration strategy,Immediate hydration for all,<LazyModal hydrate-on-visible/>,<Modal/> in footer,Low,https://nuxt.com/docs/guide/going-further/experimental-features
Can't render this file because it contains an unexpected character in line 8 and column 193.

View File

@@ -0,0 +1,52 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Components,Use functional components,Hooks-based components are standard,Functional components with hooks,Class components,const App = () => { },class App extends Component,Medium,https://reactnative.dev/docs/intro-react
2,Components,Keep components small,Single responsibility principle,Split into smaller components,Large monolithic components,<Header /><Content /><Footer />,500+ line component,Medium,
3,Components,Use TypeScript,Type safety for props and state,TypeScript for new projects,JavaScript without types,const Button: FC<Props> = () => { },const Button = (props) => { },Medium,
4,Components,Colocate component files,Keep related files together,Component folder with styles,Flat structure,components/Button/index.tsx styles.ts,components/Button.tsx styles/button.ts,Low,
5,Styling,Use StyleSheet.create,Optimized style objects,StyleSheet for all styles,Inline style objects,StyleSheet.create({ container: {} }),style={{ margin: 10 }},High,https://reactnative.dev/docs/stylesheet
6,Styling,Avoid inline styles,Prevent object recreation,Styles in StyleSheet,Inline style objects in render,style={styles.container},"style={{ margin: 10, padding: 5 }}",Medium,
7,Styling,Use flexbox for layout,React Native uses flexbox,flexDirection alignItems justifyContent,Absolute positioning everywhere,flexDirection: 'row',position: 'absolute' everywhere,Medium,https://reactnative.dev/docs/flexbox
8,Styling,Handle platform differences,Platform-specific styles,Platform.select or .ios/.android files,Same styles for both platforms,"Platform.select({ ios: {}, android: {} })",Hardcoded iOS values,Medium,https://reactnative.dev/docs/platform-specific-code
9,Styling,Use responsive dimensions,Scale for different screens,Dimensions or useWindowDimensions,Fixed pixel values,useWindowDimensions(),width: 375,Medium,
10,Navigation,Use React Navigation,Standard navigation library,React Navigation for routing,Manual navigation management,createStackNavigator(),Custom navigation state,Medium,https://reactnavigation.org/
11,Navigation,Type navigation params,Type-safe navigation,Typed navigation props,Untyped navigation,"navigation.navigate<RootStackParamList>('Home', { id })","navigation.navigate('Home', { id })",Medium,
12,Navigation,Use deep linking,Support URL-based navigation,Configure linking prop,No deep link support,linking: { prefixes: [] },No linking configuration,Medium,https://reactnavigation.org/docs/deep-linking/
13,Navigation,Handle back button,Android back button handling,useFocusEffect with BackHandler,Ignore back button,BackHandler.addEventListener,No back handler,High,
14,State,Use useState for local state,Simple component state,useState for UI state,Class component state,"const [count, setCount] = useState(0)",this.state = { count: 0 },Medium,
15,State,Use useReducer for complex state,Complex state logic,useReducer for related state,Multiple useState for related values,useReducer(reducer initialState),5+ useState calls,Medium,
16,State,Use context sparingly,Context for global state,Context for theme auth locale,Context for frequently changing data,ThemeContext for app theme,Context for list item data,Medium,
17,State,Consider Zustand or Redux,External state management,Zustand for simple Redux for complex,useState for global state,create((set) => ({ })),Prop drilling global state,Medium,
18,Lists,Use FlatList for long lists,Virtualized list rendering,FlatList for 50+ items,ScrollView with map,<FlatList data={items} />,<ScrollView>{items.map()}</ScrollView>,High,https://reactnative.dev/docs/flatlist
19,Lists,Provide keyExtractor,Unique keys for list items,keyExtractor with stable ID,Index as key,keyExtractor={(item) => item.id},"keyExtractor={(_, index) => index}",High,
20,Lists,Optimize renderItem,Memoize list item components,React.memo for list items,Inline render function,renderItem={({ item }) => <MemoizedItem item={item} />},renderItem={({ item }) => <View>...</View>},High,
21,Lists,Use getItemLayout for fixed height,Skip measurement for performance,getItemLayout when height known,Dynamic measurement for fixed items,"getItemLayout={(_, index) => ({ length: 50, offset: 50 * index, index })}",No getItemLayout for fixed height,Medium,
22,Lists,Implement windowSize,Control render window,Smaller windowSize for memory,Default windowSize for large lists,windowSize={5},windowSize={21} for huge lists,Medium,
23,Performance,Use React.memo,Prevent unnecessary re-renders,memo for pure components,No memoization,export default memo(MyComponent),export default MyComponent,Medium,
24,Performance,Use useCallback for handlers,Stable function references,useCallback for props,New function on every render,"useCallback(() => {}, [deps])",() => handlePress(),Medium,
25,Performance,Use useMemo for expensive ops,Cache expensive calculations,useMemo for heavy computations,Recalculate every render,"useMemo(() => expensive(), [deps])",const result = expensive(),Medium,
26,Performance,Avoid anonymous functions in JSX,Prevent re-renders,Named handlers or useCallback,Inline arrow functions,onPress={handlePress},onPress={() => doSomething()},Medium,
27,Performance,Use Hermes engine,Improved startup and memory,Enable Hermes in build,JavaScriptCore for new projects,hermes_enabled: true,hermes_enabled: false,Medium,https://reactnative.dev/docs/hermes
28,Images,Use expo-image,Modern performant image component for React Native,"Use expo-image for caching, blurring, and performance",Use default Image for heavy lists or unmaintained libraries,<Image source={url} cachePolicy='memory-disk' /> (expo-image),<FastImage source={url} />,Medium,https://docs.expo.dev/versions/latest/sdk/image/
29,Images,Specify image dimensions,Prevent layout shifts,width and height for remote images,No dimensions for network images,<Image style={{ width: 100 height: 100 }} />,<Image source={{ uri }} /> no size,High,
30,Images,Use resizeMode,Control image scaling,resizeMode cover contain,Stretch images,"resizeMode=""cover""",No resizeMode,Low,
31,Forms,Use controlled inputs,State-controlled form fields,value + onChangeText,Uncontrolled inputs,<TextInput value={text} onChangeText={setText} />,<TextInput defaultValue={text} />,Medium,
32,Forms,Handle keyboard,Manage keyboard visibility,KeyboardAvoidingView,Content hidden by keyboard,"<KeyboardAvoidingView behavior=""padding"">",No keyboard handling,High,https://reactnative.dev/docs/keyboardavoidingview
33,Forms,Use proper keyboard types,Appropriate keyboard for input,keyboardType for input type,Default keyboard for all,"keyboardType=""email-address""","keyboardType=""default"" for email",Low,
34,Touch,Use Pressable,Modern touch handling,Pressable for touch interactions,TouchableOpacity for new code,<Pressable onPress={} />,<TouchableOpacity onPress={} />,Low,https://reactnative.dev/docs/pressable
35,Touch,Provide touch feedback,Visual feedback on press,Ripple or opacity change,No feedback on press,android_ripple={{ color: 'gray' }},No press feedback,Medium,
36,Touch,Set hitSlop for small targets,Increase touch area,hitSlop for icons and small buttons,Tiny touch targets,hitSlop={{ top: 10 bottom: 10 }},44x44 with no hitSlop,Medium,
37,Animation,Use Reanimated,High-performance animations,react-native-reanimated,Animated API for complex,useSharedValue useAnimatedStyle,Animated.timing for gesture,Medium,https://docs.swmansion.com/react-native-reanimated/
38,Animation,Run on UI thread,worklets for smooth animation,Run animations on UI thread,JS thread animations,runOnUI(() => {}),Animated on JS thread,High,
39,Animation,Use gesture handler,Native gesture recognition,react-native-gesture-handler,JS-based gesture handling,<GestureDetector>,<View onTouchMove={} />,Medium,https://docs.swmansion.com/react-native-gesture-handler/
40,Async,Handle loading states,Show loading indicators,ActivityIndicator during load,Empty screen during load,{isLoading ? <ActivityIndicator /> : <Content />},No loading state,Medium,
41,Async,Handle errors gracefully,Error boundaries and fallbacks,Error UI for failed requests,Crash on error,{error ? <ErrorView /> : <Content />},No error handling,High,
42,Async,Cancel async operations,Cleanup on unmount,AbortController or cleanup,Memory leaks from async,useEffect cleanup,No cleanup for subscriptions,High,
43,Accessibility,Add accessibility labels,Describe UI elements,accessibilityLabel for all interactive,Missing labels,"accessibilityLabel=""Submit form""",<Pressable> without label,High,https://reactnative.dev/docs/accessibility
44,Accessibility,Use accessibility roles,Semantic meaning,accessibilityRole for elements,Wrong roles,"accessibilityRole=""button""",No role for button,Medium,
45,Accessibility,Support screen readers,Test with TalkBack/VoiceOver,Test with screen readers,Skip accessibility testing,Regular TalkBack testing,No screen reader testing,High,
46,Testing,Use React Native Testing Library,Component testing,render and fireEvent,Enzyme or manual testing,render(<Component />),shallow(<Component />),Medium,https://callstack.github.io/react-native-testing-library/
47,Testing,Test on real devices,Real device behavior,Test on iOS and Android devices,Simulator only,Device testing in CI,Simulator only testing,High,
48,Testing,Use Detox for E2E,End-to-end testing,Detox for critical flows,Manual E2E testing,detox test,Manual testing only,Medium,https://wix.github.io/Detox/
49,Native,Use native modules carefully,Bridge has overhead,Batch native calls,Frequent bridge crossing,Batch updates,Call native on every keystroke,High,
50,Native,Use Expo when possible,Simplified development,Expo for standard features,Bare RN for simple apps,expo install package,react-native link package,Low,https://docs.expo.dev/
51,Native,Handle permissions,Request permissions properly,Check and request permissions,Assume permissions granted,PermissionsAndroid.request(),Access without permission check,High,https://reactnative.dev/docs/permissionsandroid
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Components Use functional components Hooks-based components are standard Functional components with hooks Class components const App = () => { } class App extends Component Medium https://reactnative.dev/docs/intro-react
3 2 Components Keep components small Single responsibility principle Split into smaller components Large monolithic components <Header /><Content /><Footer /> 500+ line component Medium
4 3 Components Use TypeScript Type safety for props and state TypeScript for new projects JavaScript without types const Button: FC<Props> = () => { } const Button = (props) => { } Medium
5 4 Components Colocate component files Keep related files together Component folder with styles Flat structure components/Button/index.tsx styles.ts components/Button.tsx styles/button.ts Low
6 5 Styling Use StyleSheet.create Optimized style objects StyleSheet for all styles Inline style objects StyleSheet.create({ container: {} }) style={{ margin: 10 }} High https://reactnative.dev/docs/stylesheet
7 6 Styling Avoid inline styles Prevent object recreation Styles in StyleSheet Inline style objects in render style={styles.container} style={{ margin: 10, padding: 5 }} Medium
8 7 Styling Use flexbox for layout React Native uses flexbox flexDirection alignItems justifyContent Absolute positioning everywhere flexDirection: 'row' position: 'absolute' everywhere Medium https://reactnative.dev/docs/flexbox
9 8 Styling Handle platform differences Platform-specific styles Platform.select or .ios/.android files Same styles for both platforms Platform.select({ ios: {}, android: {} }) Hardcoded iOS values Medium https://reactnative.dev/docs/platform-specific-code
10 9 Styling Use responsive dimensions Scale for different screens Dimensions or useWindowDimensions Fixed pixel values useWindowDimensions() width: 375 Medium
11 10 Navigation Use React Navigation Standard navigation library React Navigation for routing Manual navigation management createStackNavigator() Custom navigation state Medium https://reactnavigation.org/
12 11 Navigation Type navigation params Type-safe navigation Typed navigation props Untyped navigation navigation.navigate<RootStackParamList>('Home', { id }) navigation.navigate('Home', { id }) Medium
13 12 Navigation Use deep linking Support URL-based navigation Configure linking prop No deep link support linking: { prefixes: [] } No linking configuration Medium https://reactnavigation.org/docs/deep-linking/
14 13 Navigation Handle back button Android back button handling useFocusEffect with BackHandler Ignore back button BackHandler.addEventListener No back handler High
15 14 State Use useState for local state Simple component state useState for UI state Class component state const [count, setCount] = useState(0) this.state = { count: 0 } Medium
16 15 State Use useReducer for complex state Complex state logic useReducer for related state Multiple useState for related values useReducer(reducer initialState) 5+ useState calls Medium
17 16 State Use context sparingly Context for global state Context for theme auth locale Context for frequently changing data ThemeContext for app theme Context for list item data Medium
18 17 State Consider Zustand or Redux External state management Zustand for simple Redux for complex useState for global state create((set) => ({ })) Prop drilling global state Medium
19 18 Lists Use FlatList for long lists Virtualized list rendering FlatList for 50+ items ScrollView with map <FlatList data={items} /> <ScrollView>{items.map()}</ScrollView> High https://reactnative.dev/docs/flatlist
20 19 Lists Provide keyExtractor Unique keys for list items keyExtractor with stable ID Index as key keyExtractor={(item) => item.id} keyExtractor={(_, index) => index} High
21 20 Lists Optimize renderItem Memoize list item components React.memo for list items Inline render function renderItem={({ item }) => <MemoizedItem item={item} />} renderItem={({ item }) => <View>...</View>} High
22 21 Lists Use getItemLayout for fixed height Skip measurement for performance getItemLayout when height known Dynamic measurement for fixed items getItemLayout={(_, index) => ({ length: 50, offset: 50 * index, index })} No getItemLayout for fixed height Medium
23 22 Lists Implement windowSize Control render window Smaller windowSize for memory Default windowSize for large lists windowSize={5} windowSize={21} for huge lists Medium
24 23 Performance Use React.memo Prevent unnecessary re-renders memo for pure components No memoization export default memo(MyComponent) export default MyComponent Medium
25 24 Performance Use useCallback for handlers Stable function references useCallback for props New function on every render useCallback(() => {}, [deps]) () => handlePress() Medium
26 25 Performance Use useMemo for expensive ops Cache expensive calculations useMemo for heavy computations Recalculate every render useMemo(() => expensive(), [deps]) const result = expensive() Medium
27 26 Performance Avoid anonymous functions in JSX Prevent re-renders Named handlers or useCallback Inline arrow functions onPress={handlePress} onPress={() => doSomething()} Medium
28 27 Performance Use Hermes engine Improved startup and memory Enable Hermes in build JavaScriptCore for new projects hermes_enabled: true hermes_enabled: false Medium https://reactnative.dev/docs/hermes
29 28 Images Use expo-image Modern performant image component for React Native Use expo-image for caching, blurring, and performance Use default Image for heavy lists or unmaintained libraries <Image source={url} cachePolicy='memory-disk' /> (expo-image) <FastImage source={url} /> Medium https://docs.expo.dev/versions/latest/sdk/image/
30 29 Images Specify image dimensions Prevent layout shifts width and height for remote images No dimensions for network images <Image style={{ width: 100 height: 100 }} /> <Image source={{ uri }} /> no size High
31 30 Images Use resizeMode Control image scaling resizeMode cover contain Stretch images resizeMode="cover" No resizeMode Low
32 31 Forms Use controlled inputs State-controlled form fields value + onChangeText Uncontrolled inputs <TextInput value={text} onChangeText={setText} /> <TextInput defaultValue={text} /> Medium
33 32 Forms Handle keyboard Manage keyboard visibility KeyboardAvoidingView Content hidden by keyboard <KeyboardAvoidingView behavior="padding"> No keyboard handling High https://reactnative.dev/docs/keyboardavoidingview
34 33 Forms Use proper keyboard types Appropriate keyboard for input keyboardType for input type Default keyboard for all keyboardType="email-address" keyboardType="default" for email Low
35 34 Touch Use Pressable Modern touch handling Pressable for touch interactions TouchableOpacity for new code <Pressable onPress={} /> <TouchableOpacity onPress={} /> Low https://reactnative.dev/docs/pressable
36 35 Touch Provide touch feedback Visual feedback on press Ripple or opacity change No feedback on press android_ripple={{ color: 'gray' }} No press feedback Medium
37 36 Touch Set hitSlop for small targets Increase touch area hitSlop for icons and small buttons Tiny touch targets hitSlop={{ top: 10 bottom: 10 }} 44x44 with no hitSlop Medium
38 37 Animation Use Reanimated High-performance animations react-native-reanimated Animated API for complex useSharedValue useAnimatedStyle Animated.timing for gesture Medium https://docs.swmansion.com/react-native-reanimated/
39 38 Animation Run on UI thread worklets for smooth animation Run animations on UI thread JS thread animations runOnUI(() => {}) Animated on JS thread High
40 39 Animation Use gesture handler Native gesture recognition react-native-gesture-handler JS-based gesture handling <GestureDetector> <View onTouchMove={} /> Medium https://docs.swmansion.com/react-native-gesture-handler/
41 40 Async Handle loading states Show loading indicators ActivityIndicator during load Empty screen during load {isLoading ? <ActivityIndicator /> : <Content />} No loading state Medium
42 41 Async Handle errors gracefully Error boundaries and fallbacks Error UI for failed requests Crash on error {error ? <ErrorView /> : <Content />} No error handling High
43 42 Async Cancel async operations Cleanup on unmount AbortController or cleanup Memory leaks from async useEffect cleanup No cleanup for subscriptions High
44 43 Accessibility Add accessibility labels Describe UI elements accessibilityLabel for all interactive Missing labels accessibilityLabel="Submit form" <Pressable> without label High https://reactnative.dev/docs/accessibility
45 44 Accessibility Use accessibility roles Semantic meaning accessibilityRole for elements Wrong roles accessibilityRole="button" No role for button Medium
46 45 Accessibility Support screen readers Test with TalkBack/VoiceOver Test with screen readers Skip accessibility testing Regular TalkBack testing No screen reader testing High
47 46 Testing Use React Native Testing Library Component testing render and fireEvent Enzyme or manual testing render(<Component />) shallow(<Component />) Medium https://callstack.github.io/react-native-testing-library/
48 47 Testing Test on real devices Real device behavior Test on iOS and Android devices Simulator only Device testing in CI Simulator only testing High
49 48 Testing Use Detox for E2E End-to-end testing Detox for critical flows Manual E2E testing detox test Manual testing only Medium https://wix.github.io/Detox/
50 49 Native Use native modules carefully Bridge has overhead Batch native calls Frequent bridge crossing Batch updates Call native on every keystroke High
51 50 Native Use Expo when possible Simplified development Expo for standard features Bare RN for simple apps expo install package react-native link package Low https://docs.expo.dev/
52 51 Native Handle permissions Request permissions properly Check and request permissions Assume permissions granted PermissionsAndroid.request() Access without permission check High https://reactnative.dev/docs/permissionsandroid

View File

@@ -0,0 +1,54 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,State,Use useState for local state,Simple component state should use useState hook,useState for form inputs toggles counters,Class components this.state,"const [count, setCount] = useState(0)",this.state = { count: 0 },Medium,https://react.dev/reference/react/useState
2,State,Lift state up when needed,Share state between siblings by lifting to parent,Lift shared state to common ancestor,Prop drilling through many levels,Parent holds state passes down,Deep prop chains,Medium,https://react.dev/learn/sharing-state-between-components
3,State,Use useReducer for complex state,Complex state logic benefits from reducer pattern,useReducer for state with multiple sub-values,Multiple useState for related values,useReducer with action types,5+ useState calls that update together,Medium,https://react.dev/reference/react/useReducer
4,State,Avoid unnecessary state,Derive values from existing state when possible,Compute derived values in render,Store derivable values in state,const total = items.reduce(...),"const [total, setTotal] = useState(0)",High,https://react.dev/learn/choosing-the-state-structure
5,State,Initialize state lazily,Use function form for expensive initial state,useState(() => computeExpensive()),useState(computeExpensive()),useState(() => JSON.parse(data)),useState(JSON.parse(data)),Medium,https://react.dev/reference/react/useState#avoiding-recreating-the-initial-state
6,Effects,Clean up effects,Return cleanup function for subscriptions timers,Return cleanup function in useEffect,No cleanup for subscriptions,useEffect(() => { sub(); return unsub; }),useEffect(() => { subscribe(); }),High,https://react.dev/reference/react/useEffect#connecting-to-an-external-system
7,Effects,Specify dependencies correctly,Include all values used inside effect in deps array,All referenced values in dependency array,Empty deps with external references,[value] when using value in effect,[] when using props/state in effect,High,https://react.dev/reference/react/useEffect#specifying-reactive-dependencies
8,Effects,Avoid unnecessary effects,Don't use effects for transforming data or events,Transform data during render handle events directly,useEffect for derived state or event handling,const filtered = items.filter(...),useEffect(() => setFiltered(items.filter(...))),High,https://react.dev/learn/you-might-not-need-an-effect
9,Effects,Use refs for non-reactive values,Store values that don't trigger re-renders in refs,useRef for interval IDs DOM elements,useState for values that don't need render,const intervalRef = useRef(null),"const [intervalId, setIntervalId] = useState()",Medium,https://react.dev/reference/react/useRef
10,Rendering,Use keys properly,Stable unique keys for list items,Use stable IDs as keys,Array index as key for dynamic lists,key={item.id},key={index},High,https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key
11,Rendering,Memoize expensive calculations,Use useMemo for costly computations,useMemo for expensive filtering/sorting,Recalculate every render,"useMemo(() => expensive(), [deps])",const result = expensiveCalc(),Medium,https://react.dev/reference/react/useMemo
12,Rendering,Memoize callbacks passed to children,Use useCallback for functions passed as props,useCallback for handlers passed to memoized children,New function reference every render,"useCallback(() => {}, [deps])",const handler = () => {},Medium,https://react.dev/reference/react/useCallback
13,Rendering,Use React.memo wisely,Wrap components that render often with same props,memo for pure components with stable props,memo everything or nothing,memo(ExpensiveList),memo(SimpleButton),Low,https://react.dev/reference/react/memo
14,Rendering,Avoid inline object/array creation in JSX,Create objects outside render or memoize,Define style objects outside component,Inline objects in props,<div style={styles.container}>,<div style={{ margin: 10 }}>,Medium,
15,Components,Keep components small and focused,Single responsibility for each component,One concern per component,Large multi-purpose components,<UserAvatar /><UserName />,<UserCard /> with 500 lines,Medium,
16,Components,Use composition over inheritance,Compose components using children and props,Use children prop for flexibility,Inheritance hierarchies,<Card>{content}</Card>,class SpecialCard extends Card,Medium,https://react.dev/learn/thinking-in-react
17,Components,Colocate related code,Keep related components and hooks together,Related files in same directory,Flat structure with many files,components/User/UserCard.tsx,components/UserCard.tsx + hooks/useUser.ts,Low,
18,Components,Use fragments to avoid extra DOM,Fragment or <> for multiple elements without wrapper,<> for grouping without DOM node,Extra div wrappers,<>{items.map(...)}</>,<div>{items.map(...)}</div>,Low,https://react.dev/reference/react/Fragment
19,Props,Destructure props,Destructure props for cleaner component code,Destructure in function signature,props.name props.value throughout,"function User({ name, age })",function User(props),Low,
20,Props,Provide default props values,Use default parameters or defaultProps,Default values in destructuring,Undefined checks throughout,function Button({ size = 'md' }),if (size === undefined) size = 'md',Low,
21,Props,Avoid prop drilling,Use context or composition for deeply nested data,Context for global data composition for UI,Passing props through 5+ levels,<UserContext.Provider>,<A user={u}><B user={u}><C user={u}>,Medium,https://react.dev/learn/passing-data-deeply-with-context
22,Props,Validate props with TypeScript,Use TypeScript interfaces for prop types,interface Props { name: string },PropTypes or no validation,interface ButtonProps { onClick: () => void },Button.propTypes = {},Medium,
23,Events,Use synthetic events correctly,React normalizes events across browsers,e.preventDefault() e.stopPropagation(),Access native event unnecessarily,onClick={(e) => e.preventDefault()},onClick={(e) => e.nativeEvent.preventDefault()},Low,https://react.dev/reference/react-dom/components/common#react-event-object
24,Events,Avoid binding in render,Use arrow functions in class or hooks,Arrow functions in functional components,bind in render or constructor,const handleClick = () => {},this.handleClick.bind(this),Medium,
25,Events,Pass event handlers not call results,Pass function reference not invocation,onClick={handleClick},onClick={handleClick()} causing immediate call,onClick={handleClick},onClick={handleClick()},High,
26,Forms,Controlled components for forms,Use state to control form inputs,value + onChange for inputs,Uncontrolled inputs with refs,<input value={val} onChange={setVal}>,<input ref={inputRef}>,Medium,https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable
27,Forms,Handle form submission properly,Prevent default and handle in submit handler,onSubmit with preventDefault,onClick on submit button only,<form onSubmit={handleSubmit}>,<button onClick={handleSubmit}>,Medium,
28,Forms,Debounce rapid input changes,Debounce search/filter inputs,useDeferredValue or debounce for search,Filter on every keystroke,useDeferredValue(searchTerm),useEffect filtering on every change,Medium,https://react.dev/reference/react/useDeferredValue
29,Hooks,Follow rules of hooks,Only call hooks at top level and in React functions,Hooks at component top level,Hooks in conditions loops or callbacks,"const [x, setX] = useState()","if (cond) { const [x, setX] = useState() }",High,https://react.dev/reference/rules/rules-of-hooks
30,Hooks,Custom hooks for reusable logic,Extract shared stateful logic to custom hooks,useCustomHook for reusable patterns,Duplicate hook logic across components,const { data } = useFetch(url),Duplicate useEffect/useState in components,Medium,https://react.dev/learn/reusing-logic-with-custom-hooks
31,Hooks,Name custom hooks with use prefix,Custom hooks must start with use,useFetch useForm useAuth,fetchData or getData for hook,function useFetch(url),function fetchData(url),High,
32,Context,Use context for global data,Context for theme auth locale,Context for app-wide state,Context for frequently changing data,<ThemeContext.Provider>,Context for form field values,Medium,https://react.dev/learn/passing-data-deeply-with-context
33,Context,Split contexts by concern,Separate contexts for different domains,ThemeContext + AuthContext,One giant AppContext,<ThemeProvider><AuthProvider>,<AppProvider value={{theme user...}}>,Medium,
34,Context,Memoize context values,Prevent unnecessary re-renders with useMemo,useMemo for context value object,New object reference every render,"value={useMemo(() => ({...}), [])}","value={{ user, theme }}",High,
35,Performance,Use React DevTools Profiler,Profile to identify performance bottlenecks,Profile before optimizing,Optimize without measuring,React DevTools Profiler,Guessing at bottlenecks,Medium,https://react.dev/learn/react-developer-tools
36,Performance,Lazy load components,Use React.lazy for code splitting,lazy() for routes and heavy components,Import everything upfront,const Page = lazy(() => import('./Page')),import Page from './Page',Medium,https://react.dev/reference/react/lazy
37,Performance,Virtualize long lists,Use windowing for lists over 100 items,react-window or react-virtual,Render thousands of DOM nodes,<VirtualizedList items={items}/>,{items.map(i => <Item />)},High,
38,Performance,Batch state updates,React 18 auto-batches but be aware,Let React batch related updates,Manual batching with flushSync,setA(1); setB(2); // batched,flushSync(() => setA(1)),Low,https://react.dev/learn/queueing-a-series-of-state-updates
39,ErrorHandling,Use error boundaries,Catch JavaScript errors in component tree,ErrorBoundary wrapping sections,Let errors crash entire app,<ErrorBoundary><App/></ErrorBoundary>,No error handling,High,https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
40,ErrorHandling,Handle async errors,Catch errors in async operations,try/catch in async handlers,Unhandled promise rejections,try { await fetch() } catch(e) {},await fetch() // no catch,High,
41,Testing,Test behavior not implementation,Test what user sees and does,Test renders and interactions,Test internal state or methods,expect(screen.getByText('Hello')),expect(component.state.name),Medium,https://testing-library.com/docs/react-testing-library/intro/
42,Testing,Use testing-library queries,Use accessible queries,getByRole getByLabelText,getByTestId for everything,getByRole('button'),getByTestId('submit-btn'),Medium,https://testing-library.com/docs/queries/about#priority
43,Accessibility,Use semantic HTML,Proper HTML elements for their purpose,button for clicks nav for navigation,div with onClick for buttons,<button onClick={...}>,<div onClick={...}>,High,https://react.dev/reference/react-dom/components#all-html-components
44,Accessibility,Manage focus properly,Handle focus for modals dialogs,Focus trap in modals return focus on close,No focus management,useEffect to focus input,Modal without focus trap,High,
45,Accessibility,Announce dynamic content,Use ARIA live regions for updates,aria-live for dynamic updates,Silent updates to screen readers,"<div aria-live=""polite"">{msg}</div>",<div>{msg}</div>,Medium,
46,Accessibility,Label form controls,Associate labels with inputs,htmlFor matching input id,Placeholder as only label,"<label htmlFor=""email"">Email</label>","<input placeholder=""Email""/>",High,
47,TypeScript,Type component props,Define interfaces for all props,interface Props with all prop types,any or missing types,interface Props { name: string },function Component(props: any),High,
48,TypeScript,Type state properly,Provide types for useState,useState<Type>() for complex state,Inferred any types,useState<User | null>(null),useState(null),Medium,
49,TypeScript,Type event handlers,Use React event types,React.ChangeEvent<HTMLInputElement>,Generic Event type,onChange: React.ChangeEvent<HTMLInputElement>,onChange: Event,Medium,
50,TypeScript,Use generics for reusable components,Generic components for flexible typing,Generic props for list components,Union types for flexibility,<List<T> items={T[]}>,<List items={any[]}>,Medium,
51,Patterns,Container/Presentational split,Separate data logic from UI,Container fetches presentational renders,Mixed data and UI in one,<UserContainer><UserView/></UserContainer>,<User /> with fetch and render,Low,
52,Patterns,Render props for flexibility,Share code via render prop pattern,Render prop for customizable rendering,Duplicate logic across components,<DataFetcher render={data => ...}/>,Copy paste fetch logic,Low,https://react.dev/reference/react/cloneElement#passing-data-with-a-render-prop
53,Patterns,Compound components,Related components sharing state,Tab + TabPanel sharing context,Prop drilling between related,<Tabs><Tab/><TabPanel/></Tabs>,<Tabs tabs={[]} panels={[...]}/>,Low,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 State Use useState for local state Simple component state should use useState hook useState for form inputs toggles counters Class components this.state const [count, setCount] = useState(0) this.state = { count: 0 } Medium https://react.dev/reference/react/useState
3 2 State Lift state up when needed Share state between siblings by lifting to parent Lift shared state to common ancestor Prop drilling through many levels Parent holds state passes down Deep prop chains Medium https://react.dev/learn/sharing-state-between-components
4 3 State Use useReducer for complex state Complex state logic benefits from reducer pattern useReducer for state with multiple sub-values Multiple useState for related values useReducer with action types 5+ useState calls that update together Medium https://react.dev/reference/react/useReducer
5 4 State Avoid unnecessary state Derive values from existing state when possible Compute derived values in render Store derivable values in state const total = items.reduce(...) const [total, setTotal] = useState(0) High https://react.dev/learn/choosing-the-state-structure
6 5 State Initialize state lazily Use function form for expensive initial state useState(() => computeExpensive()) useState(computeExpensive()) useState(() => JSON.parse(data)) useState(JSON.parse(data)) Medium https://react.dev/reference/react/useState#avoiding-recreating-the-initial-state
7 6 Effects Clean up effects Return cleanup function for subscriptions timers Return cleanup function in useEffect No cleanup for subscriptions useEffect(() => { sub(); return unsub; }) useEffect(() => { subscribe(); }) High https://react.dev/reference/react/useEffect#connecting-to-an-external-system
8 7 Effects Specify dependencies correctly Include all values used inside effect in deps array All referenced values in dependency array Empty deps with external references [value] when using value in effect [] when using props/state in effect High https://react.dev/reference/react/useEffect#specifying-reactive-dependencies
9 8 Effects Avoid unnecessary effects Don't use effects for transforming data or events Transform data during render handle events directly useEffect for derived state or event handling const filtered = items.filter(...) useEffect(() => setFiltered(items.filter(...))) High https://react.dev/learn/you-might-not-need-an-effect
10 9 Effects Use refs for non-reactive values Store values that don't trigger re-renders in refs useRef for interval IDs DOM elements useState for values that don't need render const intervalRef = useRef(null) const [intervalId, setIntervalId] = useState() Medium https://react.dev/reference/react/useRef
11 10 Rendering Use keys properly Stable unique keys for list items Use stable IDs as keys Array index as key for dynamic lists key={item.id} key={index} High https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key
12 11 Rendering Memoize expensive calculations Use useMemo for costly computations useMemo for expensive filtering/sorting Recalculate every render useMemo(() => expensive(), [deps]) const result = expensiveCalc() Medium https://react.dev/reference/react/useMemo
13 12 Rendering Memoize callbacks passed to children Use useCallback for functions passed as props useCallback for handlers passed to memoized children New function reference every render useCallback(() => {}, [deps]) const handler = () => {} Medium https://react.dev/reference/react/useCallback
14 13 Rendering Use React.memo wisely Wrap components that render often with same props memo for pure components with stable props memo everything or nothing memo(ExpensiveList) memo(SimpleButton) Low https://react.dev/reference/react/memo
15 14 Rendering Avoid inline object/array creation in JSX Create objects outside render or memoize Define style objects outside component Inline objects in props <div style={styles.container}> <div style={{ margin: 10 }}> Medium
16 15 Components Keep components small and focused Single responsibility for each component One concern per component Large multi-purpose components <UserAvatar /><UserName /> <UserCard /> with 500 lines Medium
17 16 Components Use composition over inheritance Compose components using children and props Use children prop for flexibility Inheritance hierarchies <Card>{content}</Card> class SpecialCard extends Card Medium https://react.dev/learn/thinking-in-react
18 17 Components Colocate related code Keep related components and hooks together Related files in same directory Flat structure with many files components/User/UserCard.tsx components/UserCard.tsx + hooks/useUser.ts Low
19 18 Components Use fragments to avoid extra DOM Fragment or <> for multiple elements without wrapper <> for grouping without DOM node Extra div wrappers <>{items.map(...)}</> <div>{items.map(...)}</div> Low https://react.dev/reference/react/Fragment
20 19 Props Destructure props Destructure props for cleaner component code Destructure in function signature props.name props.value throughout function User({ name, age }) function User(props) Low
21 20 Props Provide default props values Use default parameters or defaultProps Default values in destructuring Undefined checks throughout function Button({ size = 'md' }) if (size === undefined) size = 'md' Low
22 21 Props Avoid prop drilling Use context or composition for deeply nested data Context for global data composition for UI Passing props through 5+ levels <UserContext.Provider> <A user={u}><B user={u}><C user={u}> Medium https://react.dev/learn/passing-data-deeply-with-context
23 22 Props Validate props with TypeScript Use TypeScript interfaces for prop types interface Props { name: string } PropTypes or no validation interface ButtonProps { onClick: () => void } Button.propTypes = {} Medium
24 23 Events Use synthetic events correctly React normalizes events across browsers e.preventDefault() e.stopPropagation() Access native event unnecessarily onClick={(e) => e.preventDefault()} onClick={(e) => e.nativeEvent.preventDefault()} Low https://react.dev/reference/react-dom/components/common#react-event-object
25 24 Events Avoid binding in render Use arrow functions in class or hooks Arrow functions in functional components bind in render or constructor const handleClick = () => {} this.handleClick.bind(this) Medium
26 25 Events Pass event handlers not call results Pass function reference not invocation onClick={handleClick} onClick={handleClick()} causing immediate call onClick={handleClick} onClick={handleClick()} High
27 26 Forms Controlled components for forms Use state to control form inputs value + onChange for inputs Uncontrolled inputs with refs <input value={val} onChange={setVal}> <input ref={inputRef}> Medium https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable
28 27 Forms Handle form submission properly Prevent default and handle in submit handler onSubmit with preventDefault onClick on submit button only <form onSubmit={handleSubmit}> <button onClick={handleSubmit}> Medium
29 28 Forms Debounce rapid input changes Debounce search/filter inputs useDeferredValue or debounce for search Filter on every keystroke useDeferredValue(searchTerm) useEffect filtering on every change Medium https://react.dev/reference/react/useDeferredValue
30 29 Hooks Follow rules of hooks Only call hooks at top level and in React functions Hooks at component top level Hooks in conditions loops or callbacks const [x, setX] = useState() if (cond) { const [x, setX] = useState() } High https://react.dev/reference/rules/rules-of-hooks
31 30 Hooks Custom hooks for reusable logic Extract shared stateful logic to custom hooks useCustomHook for reusable patterns Duplicate hook logic across components const { data } = useFetch(url) Duplicate useEffect/useState in components Medium https://react.dev/learn/reusing-logic-with-custom-hooks
32 31 Hooks Name custom hooks with use prefix Custom hooks must start with use useFetch useForm useAuth fetchData or getData for hook function useFetch(url) function fetchData(url) High
33 32 Context Use context for global data Context for theme auth locale Context for app-wide state Context for frequently changing data <ThemeContext.Provider> Context for form field values Medium https://react.dev/learn/passing-data-deeply-with-context
34 33 Context Split contexts by concern Separate contexts for different domains ThemeContext + AuthContext One giant AppContext <ThemeProvider><AuthProvider> <AppProvider value={{theme user...}}> Medium
35 34 Context Memoize context values Prevent unnecessary re-renders with useMemo useMemo for context value object New object reference every render value={useMemo(() => ({...}), [])} value={{ user, theme }} High
36 35 Performance Use React DevTools Profiler Profile to identify performance bottlenecks Profile before optimizing Optimize without measuring React DevTools Profiler Guessing at bottlenecks Medium https://react.dev/learn/react-developer-tools
37 36 Performance Lazy load components Use React.lazy for code splitting lazy() for routes and heavy components Import everything upfront const Page = lazy(() => import('./Page')) import Page from './Page' Medium https://react.dev/reference/react/lazy
38 37 Performance Virtualize long lists Use windowing for lists over 100 items react-window or react-virtual Render thousands of DOM nodes <VirtualizedList items={items}/> {items.map(i => <Item />)} High
39 38 Performance Batch state updates React 18 auto-batches but be aware Let React batch related updates Manual batching with flushSync setA(1); setB(2); // batched flushSync(() => setA(1)) Low https://react.dev/learn/queueing-a-series-of-state-updates
40 39 ErrorHandling Use error boundaries Catch JavaScript errors in component tree ErrorBoundary wrapping sections Let errors crash entire app <ErrorBoundary><App/></ErrorBoundary> No error handling High https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
41 40 ErrorHandling Handle async errors Catch errors in async operations try/catch in async handlers Unhandled promise rejections try { await fetch() } catch(e) {} await fetch() // no catch High
42 41 Testing Test behavior not implementation Test what user sees and does Test renders and interactions Test internal state or methods expect(screen.getByText('Hello')) expect(component.state.name) Medium https://testing-library.com/docs/react-testing-library/intro/
43 42 Testing Use testing-library queries Use accessible queries getByRole getByLabelText getByTestId for everything getByRole('button') getByTestId('submit-btn') Medium https://testing-library.com/docs/queries/about#priority
44 43 Accessibility Use semantic HTML Proper HTML elements for their purpose button for clicks nav for navigation div with onClick for buttons <button onClick={...}> <div onClick={...}> High https://react.dev/reference/react-dom/components#all-html-components
45 44 Accessibility Manage focus properly Handle focus for modals dialogs Focus trap in modals return focus on close No focus management useEffect to focus input Modal without focus trap High
46 45 Accessibility Announce dynamic content Use ARIA live regions for updates aria-live for dynamic updates Silent updates to screen readers <div aria-live="polite">{msg}</div> <div>{msg}</div> Medium
47 46 Accessibility Label form controls Associate labels with inputs htmlFor matching input id Placeholder as only label <label htmlFor="email">Email</label> <input placeholder="Email"/> High
48 47 TypeScript Type component props Define interfaces for all props interface Props with all prop types any or missing types interface Props { name: string } function Component(props: any) High
49 48 TypeScript Type state properly Provide types for useState useState<Type>() for complex state Inferred any types useState<User | null>(null) useState(null) Medium
50 49 TypeScript Type event handlers Use React event types React.ChangeEvent<HTMLInputElement> Generic Event type onChange: React.ChangeEvent<HTMLInputElement> onChange: Event Medium
51 50 TypeScript Use generics for reusable components Generic components for flexible typing Generic props for list components Union types for flexibility <List<T> items={T[]}> <List items={any[]}> Medium
52 51 Patterns Container/Presentational split Separate data logic from UI Container fetches presentational renders Mixed data and UI in one <UserContainer><UserView/></UserContainer> <User /> with fetch and render Low
53 52 Patterns Render props for flexibility Share code via render prop pattern Render prop for customizable rendering Duplicate logic across components <DataFetcher render={data => ...}/> Copy paste fetch logic Low https://react.dev/reference/react/cloneElement#passing-data-with-a-render-prop
54 53 Patterns Compound components Related components sharing state Tab + TabPanel sharing context Prop drilling between related <Tabs><Tab/><TabPanel/></Tabs> <Tabs tabs={[]} panels={[...]}/> Low

View File

@@ -0,0 +1,61 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Setup,Use CLI for installation,Install components via shadcn CLI for proper setup,npx shadcn@latest add component-name,Manual copy-paste from docs,npx shadcn@latest add button,Copy component code manually,High,https://ui.shadcn.com/docs/cli
2,Setup,Initialize project properly,Run init command to set up components.json and globals.css,npx shadcn@latest init before adding components,Skip init and add components directly,npx shadcn@latest init,npx shadcn@latest add button (without init),High,https://ui.shadcn.com/docs/installation
3,Setup,Configure path aliases,Set up proper import aliases in tsconfig and components.json,Use @/components/ui path aliases,Relative imports like ../../components,import { Button } from "@/components/ui/button",import { Button } from "../../components/ui/button",Medium,https://ui.shadcn.com/docs/installation
4,Theming,Use CSS variables for colors,Define colors as CSS variables in globals.css for theming,CSS variables in :root and .dark,Hardcoded color values in components,bg-primary text-primary-foreground,bg-blue-500 text-white,High,https://ui.shadcn.com/docs/theming
5,Theming,Follow naming convention,Use semantic color names with foreground pattern,primary/primary-foreground secondary/secondary-foreground,Generic color names,--primary --primary-foreground,--blue --light-blue,Medium,https://ui.shadcn.com/docs/theming
6,Theming,Support dark mode,Include .dark class styles for all custom CSS,Define both :root and .dark color schemes,Only light mode colors,.dark { --background: 240 10% 3.9%; },No .dark class styles,High,https://ui.shadcn.com/docs/dark-mode
7,Components,Use component variants,Leverage cva variants for consistent styling,Use variant prop for different styles,Inline conditional classes,<Button variant="destructive">,<Button className={isError ? "bg-red-500" : "bg-blue-500"}>,Medium,https://ui.shadcn.com/docs/components/button
8,Components,Compose with className,Add custom classes via className prop for overrides,Extend with className for one-off customizations,Modify component source directly,<Button className="w-full">,Edit button.tsx to add w-full,Medium,https://ui.shadcn.com/docs/components/button
9,Components,Use size variants consistently,Apply size prop for consistent sizing across components,size="sm" size="lg" for sizing,Mix size classes inconsistently,<Button size="lg">,<Button className="text-lg px-8 py-4">,Medium,https://ui.shadcn.com/docs/components/button
10,Components,Prefer compound components,Use provided sub-components for complex UI,Card + CardHeader + CardContent pattern,Single component with many props,<Card><CardHeader><CardTitle>,<Card title="x" content="y" footer="z">,Medium,https://ui.shadcn.com/docs/components/card
11,Dialog,Use Dialog for modal content,Dialog component for overlay modal windows,Dialog for confirmations forms details,Alert for modal content,<Dialog><DialogContent>,<Alert> styled as modal,High,https://ui.shadcn.com/docs/components/dialog
12,Dialog,Handle dialog state properly,Use open and onOpenChange for controlled dialogs,Controlled state with useState,Uncontrolled with default open only,"<Dialog open={open} onOpenChange={setOpen}>","<Dialog defaultOpen={true}>",Medium,https://ui.shadcn.com/docs/components/dialog
13,Dialog,Include proper dialog structure,Use DialogHeader DialogTitle DialogDescription,Complete semantic structure,Missing title or description,<DialogHeader><DialogTitle><DialogDescription>,<DialogContent><p>Content</p></DialogContent>,High,https://ui.shadcn.com/docs/components/dialog
14,Sheet,Use Sheet for side panels,Sheet component for slide-out panels and drawers,Sheet for navigation filters settings,Dialog for side content,<Sheet side="right">,<Dialog> with slide animation,Medium,https://ui.shadcn.com/docs/components/sheet
15,Sheet,Specify sheet side,Set side prop for sheet slide direction,Explicit side="left" or side="right",Default side without consideration,<Sheet><SheetContent side="left">,<Sheet><SheetContent>,Low,https://ui.shadcn.com/docs/components/sheet
16,Form,Use Form with react-hook-form,Integrate Form component with react-hook-form for validation,useForm + Form + FormField pattern,Custom form handling without Form,<Form {...form}><FormField control={form.control}>,<form onSubmit={handleSubmit}>,High,https://ui.shadcn.com/docs/components/form
17,Form,Use FormField for inputs,Wrap inputs in FormField for proper labeling and errors,FormField + FormItem + FormLabel + FormControl,Input without FormField wrapper,<FormField><FormItem><FormLabel><FormControl><Input>,<Input onChange={...}>,High,https://ui.shadcn.com/docs/components/form
18,Form,Display form messages,Use FormMessage for validation error display,FormMessage after FormControl,Custom error text without FormMessage,<FormControl><Input/></FormControl><FormMessage/>,<Input/>{error && <span>{error}</span>},Medium,https://ui.shadcn.com/docs/components/form
19,Form,Use Zod for validation,Define form schema with Zod for type-safe validation,zodResolver with form schema,Manual validation logic,zodResolver(formSchema),validate: (values) => { if (!values.email) },Medium,https://ui.shadcn.com/docs/components/form
20,Select,Use Select for dropdowns,Select component for option selection,Select for choosing from list,Native select element,<Select><SelectTrigger><SelectContent>,<select><option>,Medium,https://ui.shadcn.com/docs/components/select
21,Select,Structure Select properly,Include Trigger Value Content and Items,Complete Select structure,Missing SelectValue or SelectContent,<SelectTrigger><SelectValue/></SelectTrigger><SelectContent><SelectItem>,<Select><option>,High,https://ui.shadcn.com/docs/components/select
22,Command,Use Command for search,Command component for searchable lists and palettes,Command for command palette search,Input with custom dropdown,<Command><CommandInput><CommandList>,<Input><div className="dropdown">,Medium,https://ui.shadcn.com/docs/components/command
23,Command,Group command items,Use CommandGroup for categorized items,CommandGroup with heading for sections,Flat list without grouping,<CommandGroup heading="Suggestions"><CommandItem>,<CommandItem> without groups,Low,https://ui.shadcn.com/docs/components/command
24,Table,Use Table for data display,Table component for structured data,Table for tabular data display,Div grid for table-like layouts,<Table><TableHeader><TableBody><TableRow>,<div className="grid">,Medium,https://ui.shadcn.com/docs/components/table
25,Table,Include proper table structure,Use TableHeader TableBody TableRow TableCell,Semantic table structure,Missing thead or tbody,<TableHeader><TableRow><TableHead>,<Table><TableRow> without header,High,https://ui.shadcn.com/docs/components/table
26,DataTable,Use DataTable for complex tables,Combine Table with TanStack Table for features,DataTable pattern for sorting filtering pagination,Custom table implementation,useReactTable + Table components,Custom sort filter pagination logic,Medium,https://ui.shadcn.com/docs/components/data-table
27,Tabs,Use Tabs for content switching,Tabs component for tabbed interfaces,Tabs for related content sections,Custom tab implementation,<Tabs><TabsList><TabsTrigger><TabsContent>,<div onClick={() => setTab(...)},Medium,https://ui.shadcn.com/docs/components/tabs
28,Tabs,Set default tab value,Specify defaultValue for initial tab,defaultValue on Tabs component,No default leaving first tab,<Tabs defaultValue="account">,<Tabs> without defaultValue,Low,https://ui.shadcn.com/docs/components/tabs
29,Accordion,Use Accordion for collapsible,Accordion for expandable content sections,Accordion for FAQ settings panels,Custom collapse implementation,<Accordion><AccordionItem><AccordionTrigger>,<div onClick={() => setOpen(!open)}>,Medium,https://ui.shadcn.com/docs/components/accordion
30,Accordion,Choose accordion type,Use type="single" or type="multiple" appropriately,type="single" for one open type="multiple" for many,Default type without consideration,<Accordion type="single" collapsible>,<Accordion> without type,Low,https://ui.shadcn.com/docs/components/accordion
31,Toast,Use Sonner for toasts,Sonner integration for toast notifications,toast() from sonner for notifications,Custom toast implementation,toast("Event created"),setShowToast(true),Medium,https://ui.shadcn.com/docs/components/sonner
32,Toast,Add Toaster to layout,Include Toaster component in root layout,<Toaster /> in app layout,Toaster in individual pages,app/layout.tsx: <Toaster />,page.tsx: <Toaster />,High,https://ui.shadcn.com/docs/components/sonner
33,Toast,Use toast variants,Apply toast.success toast.error for context,Semantic toast methods,Generic toast for all messages,toast.success("Saved!") toast.error("Failed"),toast("Saved!") toast("Failed"),Medium,https://ui.shadcn.com/docs/components/sonner
34,Popover,Use Popover for floating content,Popover for dropdown menus and floating panels,Popover for contextual actions,Absolute positioned divs,<Popover><PopoverTrigger><PopoverContent>,<div className="relative"><div className="absolute">,Medium,https://ui.shadcn.com/docs/components/popover
35,Popover,Handle popover alignment,Use align and side props for positioning,Explicit alignment configuration,Default alignment for all,<PopoverContent align="start" side="bottom">,<PopoverContent>,Low,https://ui.shadcn.com/docs/components/popover
36,DropdownMenu,Use DropdownMenu for actions,DropdownMenu for action lists and context menus,DropdownMenu for user menu actions,Popover for action lists,<DropdownMenu><DropdownMenuTrigger><DropdownMenuContent>,<Popover> for menu actions,Medium,https://ui.shadcn.com/docs/components/dropdown-menu
37,DropdownMenu,Group menu items,Use DropdownMenuGroup and DropdownMenuSeparator,Organized menu with separators,Flat list of items,<DropdownMenuGroup><DropdownMenuItem><DropdownMenuSeparator>,<DropdownMenuItem> without organization,Low,https://ui.shadcn.com/docs/components/dropdown-menu
38,Tooltip,Use Tooltip for hints,Tooltip for icon buttons and truncated text,Tooltip for additional context,Title attribute for tooltips,<Tooltip><TooltipTrigger><TooltipContent>,<button title="Delete">,Medium,https://ui.shadcn.com/docs/components/tooltip
39,Tooltip,Add TooltipProvider,Wrap app or section in TooltipProvider,TooltipProvider at app level,TooltipProvider per tooltip,<TooltipProvider><App/></TooltipProvider>,<Tooltip><TooltipProvider>,High,https://ui.shadcn.com/docs/components/tooltip
40,Skeleton,Use Skeleton for loading,Skeleton component for loading placeholders,Skeleton matching content layout,Spinner for content loading,<Skeleton className="h-4 w-[200px]"/>,<Spinner/> for card loading,Medium,https://ui.shadcn.com/docs/components/skeleton
41,Skeleton,Match skeleton dimensions,Size skeleton to match loaded content,Skeleton same size as expected content,Generic skeleton size,<Skeleton className="h-12 w-12 rounded-full"/>,<Skeleton/> without sizing,Medium,https://ui.shadcn.com/docs/components/skeleton
42,AlertDialog,Use AlertDialog for confirms,AlertDialog for destructive action confirmation,AlertDialog for delete confirmations,Dialog for confirmations,<AlertDialog><AlertDialogTrigger><AlertDialogContent>,<Dialog> for delete confirmation,High,https://ui.shadcn.com/docs/components/alert-dialog
43,AlertDialog,Include action buttons,Use AlertDialogAction and AlertDialogCancel,Standard confirm/cancel pattern,Custom buttons in AlertDialog,<AlertDialogCancel>Cancel</AlertDialogCancel><AlertDialogAction>,<Button>Cancel</Button><Button>Confirm</Button>,Medium,https://ui.shadcn.com/docs/components/alert-dialog
44,Sidebar,Use Sidebar for navigation,Sidebar component for app navigation,Sidebar for main app navigation,Custom sidebar implementation,<SidebarProvider><Sidebar><SidebarContent>,<div className="w-64 fixed">,Medium,https://ui.shadcn.com/docs/components/sidebar
45,Sidebar,Wrap in SidebarProvider,Use SidebarProvider for sidebar state management,SidebarProvider at layout level,Sidebar without provider,<SidebarProvider><Sidebar></SidebarProvider>,<Sidebar> without provider,High,https://ui.shadcn.com/docs/components/sidebar
46,Sidebar,Use SidebarTrigger,Include SidebarTrigger for mobile toggle,SidebarTrigger for responsive toggle,Custom toggle button,<SidebarTrigger/>,<Button onClick={() => toggleSidebar()}>,Medium,https://ui.shadcn.com/docs/components/sidebar
47,Chart,Use Chart for data viz,Chart component with Recharts integration,Chart component for dashboards,Direct Recharts without wrapper,<ChartContainer config={chartConfig}>,<ResponsiveContainer><BarChart>,Medium,https://ui.shadcn.com/docs/components/chart
48,Chart,Define chart config,Create chartConfig for consistent theming,chartConfig with color definitions,Inline colors in charts,"{ desktop: { label: ""Desktop"", color: ""#2563eb"" } }",<Bar fill="#2563eb"/>,Medium,https://ui.shadcn.com/docs/components/chart
49,Chart,Use ChartTooltip,Apply ChartTooltip for interactive charts,ChartTooltip with ChartTooltipContent,Recharts Tooltip directly,<ChartTooltip content={<ChartTooltipContent/>}/>,<Tooltip/> from recharts,Low,https://ui.shadcn.com/docs/components/chart
50,Blocks,Use blocks for scaffolding,Start from shadcn blocks for common layouts,npx shadcn@latest add dashboard-01,Build dashboard from scratch,npx shadcn@latest add login-01,Custom login page from scratch,Medium,https://ui.shadcn.com/blocks
51,Blocks,Customize block components,Modify copied block code to fit needs,Edit block files after installation,Use blocks without modification,Customize dashboard-01 layout,Use dashboard-01 as-is,Low,https://ui.shadcn.com/blocks
52,A11y,Use semantic components,Shadcn components have built-in ARIA,Rely on component accessibility,Override ARIA attributes,<Button> has button role,<div role="button">,High,https://ui.shadcn.com/docs/components/button
53,A11y,Maintain focus management,Dialog Sheet handle focus automatically,Let components manage focus,Custom focus handling,<Dialog> traps focus,document.querySelector().focus(),High,https://ui.shadcn.com/docs/components/dialog
54,A11y,Provide labels,Use FormLabel and aria-label appropriately,FormLabel for form inputs,Placeholder as only label,<FormLabel>Email</FormLabel><Input/>,<Input placeholder="Email"/>,High,https://ui.shadcn.com/docs/components/form
55,Performance,Import components individually,Import only needed components,Named imports from component files,Import all from index,import { Button } from "@/components/ui/button",import { Button Card Dialog } from "@/components/ui",Medium,
56,Performance,Lazy load dialogs,Dynamic import for heavy dialog content,React.lazy for dialog content,Import all dialogs upfront,const HeavyContent = lazy(() => import('./Heavy')),import HeavyContent from './Heavy',Medium,
57,Customization,Extend variants with cva,Add new variants using class-variance-authority,Extend buttonVariants for new styles,Inline classes for variants,"variants: { size: { xl: ""h-14 px-8"" } }",className="h-14 px-8",Medium,https://ui.shadcn.com/docs/components/button
58,Customization,Create custom components,Build new components following shadcn patterns,Use cn() and cva for custom components,Different patterns for custom,const Custom = ({ className }) => <div className={cn("base" className)}>,const Custom = ({ style }) => <div style={style}>,Medium,
59,Patterns,Use asChild for composition,asChild prop for component composition,Slot pattern with asChild,Wrapper divs for composition,<Button asChild><Link href="/">,<Button><Link href="/"></Link></Button>,Medium,https://ui.shadcn.com/docs/components/button
60,Patterns,Combine with React Hook Form,Form + useForm for complete forms,RHF Controller with shadcn inputs,Custom form state management,<FormField control={form.control} name="email">,<Input value={email} onChange={(e) => setEmail(e.target.value)},High,https://ui.shadcn.com/docs/components/form
Can't render this file because it contains an unexpected character in line 4 and column 188.

View File

@@ -0,0 +1,54 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Reactivity,Use $: for reactive statements,Automatic dependency tracking,$: for derived values,Manual recalculation,$: doubled = count * 2,let doubled; count && (doubled = count * 2),Medium,https://svelte.dev/docs/svelte-components#script-3-$-marks-a-statement-as-reactive
2,Reactivity,Trigger reactivity with assignment,Svelte tracks assignments not mutations,Reassign arrays/objects to trigger update,Mutate without reassignment,"items = [...items, newItem]",items.push(newItem),High,https://svelte.dev/docs/svelte-components#script-2-assignments-are-reactive
3,Reactivity,Use $state in Svelte 5,Runes for explicit reactivity,let count = $state(0),Implicit reactivity in Svelte 5,let count = $state(0),let count = 0 (Svelte 5),Medium,https://svelte.dev/blog/runes
4,Reactivity,Use $derived for computed values,$derived replaces $: in Svelte 5,let doubled = $derived(count * 2),$: in Svelte 5,let doubled = $derived(count * 2),$: doubled = count * 2 (Svelte 5),Medium,
5,Reactivity,Use $effect for side effects,$effect replaces $: side effects,Use $effect for subscriptions,$: for side effects in Svelte 5,$effect(() => console.log(count)),$: console.log(count) (Svelte 5),Medium,
6,Props,Export let for props,Declare props with export let,export let propName,Props without export,export let count = 0,let count = 0,High,https://svelte.dev/docs/svelte-components#script-1-export-creates-a-component-prop
7,Props,Use $props in Svelte 5,$props rune for prop access,let { name } = $props(),export let in Svelte 5,"let { name, age = 0 } = $props()",export let name; export let age = 0,Medium,
8,Props,Provide default values,Default props with assignment,export let count = 0,Required props without defaults,export let count = 0,export let count,Low,
9,Props,Use spread props,Pass through unknown props,{...$$restProps} on elements,Manual prop forwarding,<button {...$$restProps}>,<button class={$$props.class}>,Low,https://svelte.dev/docs/basic-markup#attributes-and-props
10,Bindings,Use bind: for two-way binding,Simplified input handling,bind:value for inputs,on:input with manual update,<input bind:value={name}>,<input value={name} on:input={e => name = e.target.value}>,Low,https://svelte.dev/docs/element-directives#bind-property
11,Bindings,Bind to DOM elements,Reference DOM nodes,bind:this for element reference,querySelector in onMount,<div bind:this={el}>,onMount(() => el = document.querySelector()),Medium,
12,Bindings,Use bind:group for radios/checkboxes,Simplified group handling,bind:group for radio/checkbox groups,Manual checked handling,"<input type=""radio"" bind:group={selected}>","<input type=""radio"" checked={selected === value}>",Low,
13,Events,Use on: for event handlers,Event directive syntax,on:click={handler},addEventListener in onMount,<button on:click={handleClick}>,onMount(() => btn.addEventListener()),Medium,https://svelte.dev/docs/element-directives#on-eventname
14,Events,Forward events with on:event,Pass events to parent,on:click without handler,createEventDispatcher for DOM events,<button on:click>,"dispatch('click', event)",Low,
15,Events,Use createEventDispatcher,Custom component events,dispatch for custom events,on:event for custom events,"dispatch('save', { data })",on:save without dispatch,Medium,https://svelte.dev/docs/svelte#createeventdispatcher
16,Lifecycle,Use onMount for initialization,Run code after component mounts,onMount for setup and data fetching,Code in script body for side effects,onMount(() => fetchData()),fetchData() in script body,High,https://svelte.dev/docs/svelte#onmount
17,Lifecycle,Return cleanup from onMount,Automatic cleanup on destroy,Return function from onMount,Separate onDestroy for paired cleanup,onMount(() => { sub(); return unsub }),onMount(sub); onDestroy(unsub),Medium,
18,Lifecycle,Use onDestroy sparingly,Only when onMount cleanup not possible,onDestroy for non-mount cleanup,onDestroy for mount-related cleanup,onDestroy for store unsubscribe,onDestroy(() => clearInterval(id)),Low,
19,Lifecycle,Avoid beforeUpdate/afterUpdate,Usually not needed,Reactive statements instead,beforeUpdate for derived state,$: if (x) doSomething(),beforeUpdate(() => doSomething()),Low,
20,Stores,Use writable for mutable state,Basic reactive store,writable for shared mutable state,Local variables for shared state,const count = writable(0),let count = 0 in module,Medium,https://svelte.dev/docs/svelte-store#writable
21,Stores,Use readable for read-only state,External data sources,readable for derived/external data,writable for read-only data,"readable(0, set => interval(set))",writable(0) for timer,Low,https://svelte.dev/docs/svelte-store#readable
22,Stores,Use derived for computed stores,Combine or transform stores,derived for computed values,Manual subscription for derived,"derived(count, $c => $c * 2)",count.subscribe(c => doubled = c * 2),Medium,https://svelte.dev/docs/svelte-store#derived
23,Stores,Use $ prefix for auto-subscription,Automatic subscribe/unsubscribe,$storeName in components,Manual subscription,{$count},count.subscribe(c => value = c),High,
24,Stores,Clean up custom subscriptions,Unsubscribe when component destroys,Return unsubscribe from onMount,Leave subscriptions open,onMount(() => store.subscribe(fn)),store.subscribe(fn) in script,High,
25,Slots,Use slots for composition,Content projection,<slot> for flexible content,Props for all content,<slot>Default</slot>,"<Component content=""text""/>",Medium,https://svelte.dev/docs/special-elements#slot
26,Slots,Name slots for multiple areas,Multiple content areas,"<slot name=""header"">",Single slot for complex layouts,"<slot name=""header""><slot name=""footer"">",<slot> with complex conditionals,Low,
27,Slots,Check slot content with $$slots,Conditional slot rendering,$$slots.name for conditional rendering,Always render slot wrapper,"{#if $$slots.footer}<slot name=""footer""/>{/if}","<div><slot name=""footer""/></div>",Low,
28,Styling,Use scoped styles by default,Styles scoped to component,<style> for component styles,Global styles for component,:global() only when needed,<style> all global,Medium,https://svelte.dev/docs/svelte-components#style
29,Styling,Use :global() sparingly,Escape scoping when needed,:global for third-party styling,Global for all styles,:global(.external-lib),<style> without scoping,Medium,
30,Styling,Use CSS variables for theming,Dynamic styling,CSS custom properties,Inline styles for themes,"style=""--color: {color}""","style=""color: {color}""",Low,
31,Transitions,Use built-in transitions,Svelte transition directives,transition:fade for simple effects,Manual CSS transitions,<div transition:fade>,<div class:fade={visible}>,Low,https://svelte.dev/docs/element-directives#transition-fn
32,Transitions,Use in: and out: separately,Different enter/exit animations,in:fly out:fade for asymmetric,Same transition for both,<div in:fly out:fade>,<div transition:fly>,Low,
33,Transitions,Add local modifier,Prevent ancestor trigger,transition:fade|local,Global transitions for lists,<div transition:slide|local>,<div transition:slide>,Medium,
34,Actions,Use actions for DOM behavior,Reusable DOM logic,use:action for DOM enhancements,onMount for each usage,<div use:clickOutside>,onMount(() => setupClickOutside(el)),Medium,https://svelte.dev/docs/element-directives#use-action
35,Actions,Return update and destroy,Lifecycle methods for actions,"Return { update, destroy }",Only initial setup,"return { update(params) {}, destroy() {} }",return destroy only,Medium,
36,Actions,Pass parameters to actions,Configure action behavior,use:action={params},Hardcoded action behavior,<div use:tooltip={options}>,<div use:tooltip>,Low,
37,Logic,Use {#if} for conditionals,Template conditionals,{#if} {:else if} {:else},Ternary in expressions,{#if cond}...{:else}...{/if},{cond ? a : b} for complex,Low,https://svelte.dev/docs/logic-blocks#if
38,Logic,Use {#each} for lists,List rendering,{#each} with key,Map in expression,{#each items as item (item.id)},{items.map(i => `<div>${i}</div>`)},Medium,
39,Logic,Always use keys in {#each},Proper list reconciliation,(item.id) for unique key,Index as key or no key,{#each items as item (item.id)},"{#each items as item, i (i)}",High,
40,Logic,Use {#await} for promises,Handle async states,{#await} for loading/error states,Manual promise handling,{#await promise}...{:then}...{:catch},{#if loading}...{#if error},Medium,https://svelte.dev/docs/logic-blocks#await
41,SvelteKit,Use +page.svelte for routes,File-based routing,+page.svelte for route components,Custom routing setup,routes/about/+page.svelte,routes/About.svelte,Medium,https://kit.svelte.dev/docs/routing
42,SvelteKit,Use +page.js for data loading,Load data before render,load function in +page.js,onMount for data fetching,export function load() {},onMount(() => fetchData()),High,https://kit.svelte.dev/docs/load
43,SvelteKit,Use +page.server.js for server-only,Server-side data loading,+page.server.js for sensitive data,+page.js for API keys,+page.server.js with DB access,+page.js with DB access,High,
44,SvelteKit,Use form actions,Server-side form handling,+page.server.js actions,API routes for forms,export const actions = { default },fetch('/api/submit'),Medium,https://kit.svelte.dev/docs/form-actions
45,SvelteKit,Use $app/stores for app state,$page $navigating $updated,$page for current page data,Manual URL parsing,import { page } from '$app/stores',window.location.pathname,Medium,https://kit.svelte.dev/docs/modules#$app-stores
46,Performance,Use {#key} for forced re-render,Reset component state,{#key id} for fresh instance,Manual destroy/create,{#key item.id}<Component/>{/key},on:change={() => component = null},Low,https://svelte.dev/docs/logic-blocks#key
47,Performance,Avoid unnecessary reactivity,Not everything needs $:,$: only for side effects,$: for simple assignments,$: if (x) console.log(x),$: y = x (when y = x works),Low,
48,Performance,Use immutable compiler option,Skip equality checks,immutable: true for large lists,Default for all components,<svelte:options immutable/>,Default without immutable,Low,
49,TypeScript,"Use lang=""ts"" in script",TypeScript support,"<script lang=""ts"">",JavaScript for typed projects,"<script lang=""ts"">",<script> with JSDoc,Medium,https://svelte.dev/docs/typescript
50,TypeScript,Type props with interface,Explicit prop types,interface $$Props for types,Untyped props,interface $$Props { name: string },export let name,Medium,
51,TypeScript,Type events with createEventDispatcher,Type-safe events,createEventDispatcher<Events>(),Untyped dispatch,createEventDispatcher<{ save: Data }>(),createEventDispatcher(),Medium,
52,Accessibility,Use semantic elements,Proper HTML in templates,button nav main appropriately,div for everything,<button on:click>,<div on:click>,High,
53,Accessibility,Add aria to dynamic content,Accessible state changes,aria-live for updates,Silent dynamic updates,"<div aria-live=""polite"">{message}</div>",<div>{message}</div>,Medium,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Reactivity Use $: for reactive statements Automatic dependency tracking $: for derived values Manual recalculation $: doubled = count * 2 let doubled; count && (doubled = count * 2) Medium https://svelte.dev/docs/svelte-components#script-3-$-marks-a-statement-as-reactive
3 2 Reactivity Trigger reactivity with assignment Svelte tracks assignments not mutations Reassign arrays/objects to trigger update Mutate without reassignment items = [...items, newItem] items.push(newItem) High https://svelte.dev/docs/svelte-components#script-2-assignments-are-reactive
4 3 Reactivity Use $state in Svelte 5 Runes for explicit reactivity let count = $state(0) Implicit reactivity in Svelte 5 let count = $state(0) let count = 0 (Svelte 5) Medium https://svelte.dev/blog/runes
5 4 Reactivity Use $derived for computed values $derived replaces $: in Svelte 5 let doubled = $derived(count * 2) $: in Svelte 5 let doubled = $derived(count * 2) $: doubled = count * 2 (Svelte 5) Medium
6 5 Reactivity Use $effect for side effects $effect replaces $: side effects Use $effect for subscriptions $: for side effects in Svelte 5 $effect(() => console.log(count)) $: console.log(count) (Svelte 5) Medium
7 6 Props Export let for props Declare props with export let export let propName Props without export export let count = 0 let count = 0 High https://svelte.dev/docs/svelte-components#script-1-export-creates-a-component-prop
8 7 Props Use $props in Svelte 5 $props rune for prop access let { name } = $props() export let in Svelte 5 let { name, age = 0 } = $props() export let name; export let age = 0 Medium
9 8 Props Provide default values Default props with assignment export let count = 0 Required props without defaults export let count = 0 export let count Low
10 9 Props Use spread props Pass through unknown props {...$$restProps} on elements Manual prop forwarding <button {...$$restProps}> <button class={$$props.class}> Low https://svelte.dev/docs/basic-markup#attributes-and-props
11 10 Bindings Use bind: for two-way binding Simplified input handling bind:value for inputs on:input with manual update <input bind:value={name}> <input value={name} on:input={e => name = e.target.value}> Low https://svelte.dev/docs/element-directives#bind-property
12 11 Bindings Bind to DOM elements Reference DOM nodes bind:this for element reference querySelector in onMount <div bind:this={el}> onMount(() => el = document.querySelector()) Medium
13 12 Bindings Use bind:group for radios/checkboxes Simplified group handling bind:group for radio/checkbox groups Manual checked handling <input type="radio" bind:group={selected}> <input type="radio" checked={selected === value}> Low
14 13 Events Use on: for event handlers Event directive syntax on:click={handler} addEventListener in onMount <button on:click={handleClick}> onMount(() => btn.addEventListener()) Medium https://svelte.dev/docs/element-directives#on-eventname
15 14 Events Forward events with on:event Pass events to parent on:click without handler createEventDispatcher for DOM events <button on:click> dispatch('click', event) Low
16 15 Events Use createEventDispatcher Custom component events dispatch for custom events on:event for custom events dispatch('save', { data }) on:save without dispatch Medium https://svelte.dev/docs/svelte#createeventdispatcher
17 16 Lifecycle Use onMount for initialization Run code after component mounts onMount for setup and data fetching Code in script body for side effects onMount(() => fetchData()) fetchData() in script body High https://svelte.dev/docs/svelte#onmount
18 17 Lifecycle Return cleanup from onMount Automatic cleanup on destroy Return function from onMount Separate onDestroy for paired cleanup onMount(() => { sub(); return unsub }) onMount(sub); onDestroy(unsub) Medium
19 18 Lifecycle Use onDestroy sparingly Only when onMount cleanup not possible onDestroy for non-mount cleanup onDestroy for mount-related cleanup onDestroy for store unsubscribe onDestroy(() => clearInterval(id)) Low
20 19 Lifecycle Avoid beforeUpdate/afterUpdate Usually not needed Reactive statements instead beforeUpdate for derived state $: if (x) doSomething() beforeUpdate(() => doSomething()) Low
21 20 Stores Use writable for mutable state Basic reactive store writable for shared mutable state Local variables for shared state const count = writable(0) let count = 0 in module Medium https://svelte.dev/docs/svelte-store#writable
22 21 Stores Use readable for read-only state External data sources readable for derived/external data writable for read-only data readable(0, set => interval(set)) writable(0) for timer Low https://svelte.dev/docs/svelte-store#readable
23 22 Stores Use derived for computed stores Combine or transform stores derived for computed values Manual subscription for derived derived(count, $c => $c * 2) count.subscribe(c => doubled = c * 2) Medium https://svelte.dev/docs/svelte-store#derived
24 23 Stores Use $ prefix for auto-subscription Automatic subscribe/unsubscribe $storeName in components Manual subscription {$count} count.subscribe(c => value = c) High
25 24 Stores Clean up custom subscriptions Unsubscribe when component destroys Return unsubscribe from onMount Leave subscriptions open onMount(() => store.subscribe(fn)) store.subscribe(fn) in script High
26 25 Slots Use slots for composition Content projection <slot> for flexible content Props for all content <slot>Default</slot> <Component content="text"/> Medium https://svelte.dev/docs/special-elements#slot
27 26 Slots Name slots for multiple areas Multiple content areas <slot name="header"> Single slot for complex layouts <slot name="header"><slot name="footer"> <slot> with complex conditionals Low
28 27 Slots Check slot content with $$slots Conditional slot rendering $$slots.name for conditional rendering Always render slot wrapper {#if $$slots.footer}<slot name="footer"/>{/if} <div><slot name="footer"/></div> Low
29 28 Styling Use scoped styles by default Styles scoped to component <style> for component styles Global styles for component :global() only when needed <style> all global Medium https://svelte.dev/docs/svelte-components#style
30 29 Styling Use :global() sparingly Escape scoping when needed :global for third-party styling Global for all styles :global(.external-lib) <style> without scoping Medium
31 30 Styling Use CSS variables for theming Dynamic styling CSS custom properties Inline styles for themes style="--color: {color}" style="color: {color}" Low
32 31 Transitions Use built-in transitions Svelte transition directives transition:fade for simple effects Manual CSS transitions <div transition:fade> <div class:fade={visible}> Low https://svelte.dev/docs/element-directives#transition-fn
33 32 Transitions Use in: and out: separately Different enter/exit animations in:fly out:fade for asymmetric Same transition for both <div in:fly out:fade> <div transition:fly> Low
34 33 Transitions Add local modifier Prevent ancestor trigger transition:fade|local Global transitions for lists <div transition:slide|local> <div transition:slide> Medium
35 34 Actions Use actions for DOM behavior Reusable DOM logic use:action for DOM enhancements onMount for each usage <div use:clickOutside> onMount(() => setupClickOutside(el)) Medium https://svelte.dev/docs/element-directives#use-action
36 35 Actions Return update and destroy Lifecycle methods for actions Return { update, destroy } Only initial setup return { update(params) {}, destroy() {} } return destroy only Medium
37 36 Actions Pass parameters to actions Configure action behavior use:action={params} Hardcoded action behavior <div use:tooltip={options}> <div use:tooltip> Low
38 37 Logic Use {#if} for conditionals Template conditionals {#if} {:else if} {:else} Ternary in expressions {#if cond}...{:else}...{/if} {cond ? a : b} for complex Low https://svelte.dev/docs/logic-blocks#if
39 38 Logic Use {#each} for lists List rendering {#each} with key Map in expression {#each items as item (item.id)} {items.map(i => `<div>${i}</div>`)} Medium
40 39 Logic Always use keys in {#each} Proper list reconciliation (item.id) for unique key Index as key or no key {#each items as item (item.id)} {#each items as item, i (i)} High
41 40 Logic Use {#await} for promises Handle async states {#await} for loading/error states Manual promise handling {#await promise}...{:then}...{:catch} {#if loading}...{#if error} Medium https://svelte.dev/docs/logic-blocks#await
42 41 SvelteKit Use +page.svelte for routes File-based routing +page.svelte for route components Custom routing setup routes/about/+page.svelte routes/About.svelte Medium https://kit.svelte.dev/docs/routing
43 42 SvelteKit Use +page.js for data loading Load data before render load function in +page.js onMount for data fetching export function load() {} onMount(() => fetchData()) High https://kit.svelte.dev/docs/load
44 43 SvelteKit Use +page.server.js for server-only Server-side data loading +page.server.js for sensitive data +page.js for API keys +page.server.js with DB access +page.js with DB access High
45 44 SvelteKit Use form actions Server-side form handling +page.server.js actions API routes for forms export const actions = { default } fetch('/api/submit') Medium https://kit.svelte.dev/docs/form-actions
46 45 SvelteKit Use $app/stores for app state $page $navigating $updated $page for current page data Manual URL parsing import { page } from '$app/stores' window.location.pathname Medium https://kit.svelte.dev/docs/modules#$app-stores
47 46 Performance Use {#key} for forced re-render Reset component state {#key id} for fresh instance Manual destroy/create {#key item.id}<Component/>{/key} on:change={() => component = null} Low https://svelte.dev/docs/logic-blocks#key
48 47 Performance Avoid unnecessary reactivity Not everything needs $: $: only for side effects $: for simple assignments $: if (x) console.log(x) $: y = x (when y = x works) Low
49 48 Performance Use immutable compiler option Skip equality checks immutable: true for large lists Default for all components <svelte:options immutable/> Default without immutable Low
50 49 TypeScript Use lang="ts" in script TypeScript support <script lang="ts"> JavaScript for typed projects <script lang="ts"> <script> with JSDoc Medium https://svelte.dev/docs/typescript
51 50 TypeScript Type props with interface Explicit prop types interface $$Props for types Untyped props interface $$Props { name: string } export let name Medium
52 51 TypeScript Type events with createEventDispatcher Type-safe events createEventDispatcher<Events>() Untyped dispatch createEventDispatcher<{ save: Data }>() createEventDispatcher() Medium
53 52 Accessibility Use semantic elements Proper HTML in templates button nav main appropriately div for everything <button on:click> <div on:click> High
54 53 Accessibility Add aria to dynamic content Accessible state changes aria-live for updates Silent dynamic updates <div aria-live="polite">{message}</div> <div>{message}</div> Medium

View File

@@ -0,0 +1,51 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Views,Use struct for views,SwiftUI views are value types,struct MyView: View,class MyView: View,struct ContentView: View { var body: some View },class ContentView: View,High,https://developer.apple.com/documentation/swiftui/view
2,Views,Keep views small and focused,Single responsibility for each view,Extract subviews for complex layouts,Large monolithic views,Extract HeaderView FooterView,500+ line View struct,Medium,
3,Views,Use body computed property,body returns the view hierarchy,var body: some View { },func body() -> some View,"var body: some View { Text(""Hello"") }",func body() -> Text,High,
4,Views,Prefer composition over inheritance,Compose views using ViewBuilder,Combine smaller views,Inheritance hierarchies,VStack { Header() Content() },class SpecialView extends BaseView,Medium,
5,State,Use @State for local state,Simple value types owned by view,@State for view-local primitives,@State for shared data,@State private var count = 0,@State var sharedData: Model,High,https://developer.apple.com/documentation/swiftui/state
6,State,Use @Binding for two-way data,Pass mutable state to child views,@Binding for child input,@State in child for parent data,@Binding var isOn: Bool,$isOn to pass binding,Medium,https://developer.apple.com/documentation/swiftui/binding
7,State,Use @StateObject for reference types,ObservableObject owned by view,@StateObject for view-created objects,@ObservedObject for owned objects,@StateObject private var vm = ViewModel(),@ObservedObject var vm = ViewModel(),High,https://developer.apple.com/documentation/swiftui/stateobject
8,State,Use @ObservedObject for injected objects,Reference types passed from parent,@ObservedObject for injected dependencies,@StateObject for injected objects,@ObservedObject var vm: ViewModel,@StateObject var vm: ViewModel (injected),High,https://developer.apple.com/documentation/swiftui/observedobject
9,State,Use @EnvironmentObject for shared state,App-wide state injection,@EnvironmentObject for global state,Prop drilling through views,@EnvironmentObject var settings: Settings,Pass settings through 5 views,Medium,https://developer.apple.com/documentation/swiftui/environmentobject
10,State,Use @Published in ObservableObject,Automatically publish property changes,@Published for observed properties,Manual objectWillChange calls,@Published var items: [Item] = [],var items: [Item] { didSet { objectWillChange.send() } },Medium,
11,Observable,Use @Observable macro (iOS 17+),Modern observation without Combine,@Observable class for view models,ObservableObject for new projects,@Observable class ViewModel { },class ViewModel: ObservableObject,Medium,https://developer.apple.com/documentation/observation
12,Observable,Use @Bindable for @Observable,Create bindings from @Observable,@Bindable var vm for bindings,@Binding with @Observable,@Bindable var viewModel,$viewModel.name with @Observable,Medium,
13,Layout,Use VStack HStack ZStack,Standard stack-based layouts,Stacks for linear arrangements,GeometryReader for simple layouts,VStack { Text() Image() },GeometryReader for vertical list,Medium,https://developer.apple.com/documentation/swiftui/vstack
14,Layout,Use LazyVStack LazyHStack for lists,Lazy loading for performance,Lazy stacks for long lists,Regular stacks for 100+ items,LazyVStack { ForEach(items) },VStack { ForEach(largeArray) },High,https://developer.apple.com/documentation/swiftui/lazyvstack
15,Layout,Use GeometryReader sparingly,Only when needed for sizing,GeometryReader for responsive layouts,GeometryReader everywhere,GeometryReader for aspect ratio,GeometryReader wrapping everything,Medium,
16,Layout,Use spacing and padding consistently,Consistent spacing throughout app,Design system spacing values,Magic numbers for spacing,.padding(16) or .padding(),".padding(13), .padding(17)",Low,
17,Layout,Use frame modifiers correctly,Set explicit sizes when needed,.frame(maxWidth: .infinity),Fixed sizes for responsive content,.frame(maxWidth: .infinity),.frame(width: 375),Medium,
18,Modifiers,Order modifiers correctly,Modifier order affects rendering,Background before padding for full coverage,Wrong modifier order,.padding().background(Color.red),.background(Color.red).padding(),High,
19,Modifiers,Create custom ViewModifiers,Reusable modifier combinations,ViewModifier for repeated styling,Duplicate modifier chains,struct CardStyle: ViewModifier,.shadow().cornerRadius() everywhere,Medium,https://developer.apple.com/documentation/swiftui/viewmodifier
20,Modifiers,Use conditional modifiers carefully,Avoid changing view identity,if-else with same view type,Conditional that changes view identity,Text(title).foregroundColor(isActive ? .blue : .gray),if isActive { Text().bold() } else { Text() },Medium,
21,Navigation,Use NavigationStack (iOS 16+),Modern navigation with type-safe paths,NavigationStack with navigationDestination,NavigationView for new projects,NavigationStack { },NavigationView { } (deprecated),Medium,https://developer.apple.com/documentation/swiftui/navigationstack
22,Navigation,Use navigationDestination,Type-safe navigation destinations,.navigationDestination(for:),NavigationLink(destination:),.navigationDestination(for: Item.self),NavigationLink(destination: DetailView()),Medium,
23,Navigation,Use @Environment for dismiss,Programmatic navigation dismissal,@Environment(\.dismiss) var dismiss,presentationMode (deprecated),@Environment(\.dismiss) var dismiss,@Environment(\.presentationMode),Low,
24,Lists,Use List for scrollable content,Built-in scrolling and styling,List for standard scrollable content,ScrollView + VStack for simple lists,List { ForEach(items) { } },ScrollView { VStack { ForEach } },Low,https://developer.apple.com/documentation/swiftui/list
25,Lists,Provide stable identifiers,Use Identifiable or explicit id,Identifiable protocol or id parameter,Index as identifier,ForEach(items) where Item: Identifiable,"ForEach(items.indices, id: \.self)",High,
26,Lists,Use onDelete and onMove,Standard list editing,onDelete for swipe to delete,Custom delete implementation,.onDelete(perform: delete),.onTapGesture for delete,Low,
27,Forms,Use Form for settings,Grouped input controls,Form for settings screens,Manual grouping for forms,Form { Section { Toggle() } },VStack { Toggle() },Low,https://developer.apple.com/documentation/swiftui/form
28,Forms,Use @FocusState for keyboard,Manage keyboard focus,@FocusState for text field focus,Manual first responder handling,@FocusState private var isFocused: Bool,UIKit first responder,Medium,https://developer.apple.com/documentation/swiftui/focusstate
29,Forms,Validate input properly,Show validation feedback,Real-time validation feedback,Submit without validation,TextField with validation state,TextField without error handling,Medium,
30,Async,Use .task for async work,Automatic cancellation on view disappear,.task for view lifecycle async,onAppear with Task,.task { await loadData() },onAppear { Task { await loadData() } },Medium,https://developer.apple.com/documentation/swiftui/view/task(priority:_:)
31,Async,Handle loading states,Show progress during async operations,ProgressView during loading,Empty view during load,if isLoading { ProgressView() },No loading indicator,Medium,
32,Async,Use @MainActor for UI updates,Ensure UI updates on main thread,@MainActor on view models,Manual DispatchQueue.main,@MainActor class ViewModel,DispatchQueue.main.async,Medium,
33,Animation,Use withAnimation,Animate state changes,withAnimation for state transitions,No animation for state changes,withAnimation { isExpanded.toggle() },isExpanded.toggle(),Low,https://developer.apple.com/documentation/swiftui/withanimation(_:_:)
34,Animation,Use .animation modifier,Apply animations to views,.animation(.spring()) on view,Manual animation timing,.animation(.easeInOut),CABasicAnimation equivalent,Low,
35,Animation,Respect reduced motion,Check accessibility settings,Check accessibilityReduceMotion,Ignore motion preferences,@Environment(\.accessibilityReduceMotion),Always animate regardless,High,
36,Preview,Use #Preview macro (Xcode 15+),Modern preview syntax,#Preview for view previews,PreviewProvider protocol,#Preview { ContentView() },struct ContentView_Previews: PreviewProvider,Low,
37,Preview,Create multiple previews,Test different states and devices,Multiple previews for states,Single preview only,"#Preview(""Light"") { } #Preview(""Dark"") { }",Single preview configuration,Low,
38,Preview,Use preview data,Dedicated preview mock data,Static preview data,Production data in previews,Item.preview for preview,Fetch real data in preview,Low,
39,Performance,Avoid expensive body computations,Body should be fast to compute,Precompute in view model,Heavy computation in body,vm.computedValue in body,Complex calculation in body,High,
40,Performance,Use Equatable views,Skip unnecessary view updates,Equatable for complex views,Default equality for all views,struct MyView: View Equatable,No Equatable conformance,Medium,
41,Performance,Profile with Instruments,Measure before optimizing,Use SwiftUI Instruments,Guess at performance issues,Profile with Instruments,Optimize without measuring,Medium,
42,Accessibility,Add accessibility labels,Describe UI elements,.accessibilityLabel for context,Missing labels,".accessibilityLabel(""Close button"")",Button without label,High,https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(_:)-1d7jv
43,Accessibility,Support Dynamic Type,Respect text size preferences,Scalable fonts and layouts,Fixed font sizes,.font(.body) with Dynamic Type,.font(.system(size: 16)),High,
44,Accessibility,Use semantic views,Proper accessibility traits,Correct accessibilityTraits,Wrong semantic meaning,Button for actions Image for display,Image that acts like button,Medium,
45,Testing,Use ViewInspector for testing,Third-party view testing,ViewInspector for unit tests,UI tests only,ViewInspector assertions,Only XCUITest,Medium,
46,Testing,Test view models,Unit test business logic,XCTest for view model,Skip view model testing,Test ViewModel methods,No unit tests,Medium,
47,Testing,Use preview as visual test,Previews catch visual regressions,Multiple preview configurations,No visual verification,Preview different states,Single preview only,Low,
48,Architecture,Use MVVM pattern,Separate view and logic,ViewModel for business logic,Logic in View,ObservableObject ViewModel,@State for complex logic,Medium,
49,Architecture,Keep views dumb,Views display view model state,View reads from ViewModel,Business logic in View,view.items from vm.items,Complex filtering in View,Medium,
50,Architecture,Use dependency injection,Inject dependencies for testing,Initialize with dependencies,Hard-coded dependencies,init(service: ServiceProtocol),let service = RealService(),Medium,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Views Use struct for views SwiftUI views are value types struct MyView: View class MyView: View struct ContentView: View { var body: some View } class ContentView: View High https://developer.apple.com/documentation/swiftui/view
3 2 Views Keep views small and focused Single responsibility for each view Extract subviews for complex layouts Large monolithic views Extract HeaderView FooterView 500+ line View struct Medium
4 3 Views Use body computed property body returns the view hierarchy var body: some View { } func body() -> some View var body: some View { Text("Hello") } func body() -> Text High
5 4 Views Prefer composition over inheritance Compose views using ViewBuilder Combine smaller views Inheritance hierarchies VStack { Header() Content() } class SpecialView extends BaseView Medium
6 5 State Use @State for local state Simple value types owned by view @State for view-local primitives @State for shared data @State private var count = 0 @State var sharedData: Model High https://developer.apple.com/documentation/swiftui/state
7 6 State Use @Binding for two-way data Pass mutable state to child views @Binding for child input @State in child for parent data @Binding var isOn: Bool $isOn to pass binding Medium https://developer.apple.com/documentation/swiftui/binding
8 7 State Use @StateObject for reference types ObservableObject owned by view @StateObject for view-created objects @ObservedObject for owned objects @StateObject private var vm = ViewModel() @ObservedObject var vm = ViewModel() High https://developer.apple.com/documentation/swiftui/stateobject
9 8 State Use @ObservedObject for injected objects Reference types passed from parent @ObservedObject for injected dependencies @StateObject for injected objects @ObservedObject var vm: ViewModel @StateObject var vm: ViewModel (injected) High https://developer.apple.com/documentation/swiftui/observedobject
10 9 State Use @EnvironmentObject for shared state App-wide state injection @EnvironmentObject for global state Prop drilling through views @EnvironmentObject var settings: Settings Pass settings through 5 views Medium https://developer.apple.com/documentation/swiftui/environmentobject
11 10 State Use @Published in ObservableObject Automatically publish property changes @Published for observed properties Manual objectWillChange calls @Published var items: [Item] = [] var items: [Item] { didSet { objectWillChange.send() } } Medium
12 11 Observable Use @Observable macro (iOS 17+) Modern observation without Combine @Observable class for view models ObservableObject for new projects @Observable class ViewModel { } class ViewModel: ObservableObject Medium https://developer.apple.com/documentation/observation
13 12 Observable Use @Bindable for @Observable Create bindings from @Observable @Bindable var vm for bindings @Binding with @Observable @Bindable var viewModel $viewModel.name with @Observable Medium
14 13 Layout Use VStack HStack ZStack Standard stack-based layouts Stacks for linear arrangements GeometryReader for simple layouts VStack { Text() Image() } GeometryReader for vertical list Medium https://developer.apple.com/documentation/swiftui/vstack
15 14 Layout Use LazyVStack LazyHStack for lists Lazy loading for performance Lazy stacks for long lists Regular stacks for 100+ items LazyVStack { ForEach(items) } VStack { ForEach(largeArray) } High https://developer.apple.com/documentation/swiftui/lazyvstack
16 15 Layout Use GeometryReader sparingly Only when needed for sizing GeometryReader for responsive layouts GeometryReader everywhere GeometryReader for aspect ratio GeometryReader wrapping everything Medium
17 16 Layout Use spacing and padding consistently Consistent spacing throughout app Design system spacing values Magic numbers for spacing .padding(16) or .padding() .padding(13), .padding(17) Low
18 17 Layout Use frame modifiers correctly Set explicit sizes when needed .frame(maxWidth: .infinity) Fixed sizes for responsive content .frame(maxWidth: .infinity) .frame(width: 375) Medium
19 18 Modifiers Order modifiers correctly Modifier order affects rendering Background before padding for full coverage Wrong modifier order .padding().background(Color.red) .background(Color.red).padding() High
20 19 Modifiers Create custom ViewModifiers Reusable modifier combinations ViewModifier for repeated styling Duplicate modifier chains struct CardStyle: ViewModifier .shadow().cornerRadius() everywhere Medium https://developer.apple.com/documentation/swiftui/viewmodifier
21 20 Modifiers Use conditional modifiers carefully Avoid changing view identity if-else with same view type Conditional that changes view identity Text(title).foregroundColor(isActive ? .blue : .gray) if isActive { Text().bold() } else { Text() } Medium
22 21 Navigation Use NavigationStack (iOS 16+) Modern navigation with type-safe paths NavigationStack with navigationDestination NavigationView for new projects NavigationStack { } NavigationView { } (deprecated) Medium https://developer.apple.com/documentation/swiftui/navigationstack
23 22 Navigation Use navigationDestination Type-safe navigation destinations .navigationDestination(for:) NavigationLink(destination:) .navigationDestination(for: Item.self) NavigationLink(destination: DetailView()) Medium
24 23 Navigation Use @Environment for dismiss Programmatic navigation dismissal @Environment(\.dismiss) var dismiss presentationMode (deprecated) @Environment(\.dismiss) var dismiss @Environment(\.presentationMode) Low
25 24 Lists Use List for scrollable content Built-in scrolling and styling List for standard scrollable content ScrollView + VStack for simple lists List { ForEach(items) { } } ScrollView { VStack { ForEach } } Low https://developer.apple.com/documentation/swiftui/list
26 25 Lists Provide stable identifiers Use Identifiable or explicit id Identifiable protocol or id parameter Index as identifier ForEach(items) where Item: Identifiable ForEach(items.indices, id: \.self) High
27 26 Lists Use onDelete and onMove Standard list editing onDelete for swipe to delete Custom delete implementation .onDelete(perform: delete) .onTapGesture for delete Low
28 27 Forms Use Form for settings Grouped input controls Form for settings screens Manual grouping for forms Form { Section { Toggle() } } VStack { Toggle() } Low https://developer.apple.com/documentation/swiftui/form
29 28 Forms Use @FocusState for keyboard Manage keyboard focus @FocusState for text field focus Manual first responder handling @FocusState private var isFocused: Bool UIKit first responder Medium https://developer.apple.com/documentation/swiftui/focusstate
30 29 Forms Validate input properly Show validation feedback Real-time validation feedback Submit without validation TextField with validation state TextField without error handling Medium
31 30 Async Use .task for async work Automatic cancellation on view disappear .task for view lifecycle async onAppear with Task .task { await loadData() } onAppear { Task { await loadData() } } Medium https://developer.apple.com/documentation/swiftui/view/task(priority:_:)
32 31 Async Handle loading states Show progress during async operations ProgressView during loading Empty view during load if isLoading { ProgressView() } No loading indicator Medium
33 32 Async Use @MainActor for UI updates Ensure UI updates on main thread @MainActor on view models Manual DispatchQueue.main @MainActor class ViewModel DispatchQueue.main.async Medium
34 33 Animation Use withAnimation Animate state changes withAnimation for state transitions No animation for state changes withAnimation { isExpanded.toggle() } isExpanded.toggle() Low https://developer.apple.com/documentation/swiftui/withanimation(_:_:)
35 34 Animation Use .animation modifier Apply animations to views .animation(.spring()) on view Manual animation timing .animation(.easeInOut) CABasicAnimation equivalent Low
36 35 Animation Respect reduced motion Check accessibility settings Check accessibilityReduceMotion Ignore motion preferences @Environment(\.accessibilityReduceMotion) Always animate regardless High
37 36 Preview Use #Preview macro (Xcode 15+) Modern preview syntax #Preview for view previews PreviewProvider protocol #Preview { ContentView() } struct ContentView_Previews: PreviewProvider Low
38 37 Preview Create multiple previews Test different states and devices Multiple previews for states Single preview only #Preview("Light") { } #Preview("Dark") { } Single preview configuration Low
39 38 Preview Use preview data Dedicated preview mock data Static preview data Production data in previews Item.preview for preview Fetch real data in preview Low
40 39 Performance Avoid expensive body computations Body should be fast to compute Precompute in view model Heavy computation in body vm.computedValue in body Complex calculation in body High
41 40 Performance Use Equatable views Skip unnecessary view updates Equatable for complex views Default equality for all views struct MyView: View Equatable No Equatable conformance Medium
42 41 Performance Profile with Instruments Measure before optimizing Use SwiftUI Instruments Guess at performance issues Profile with Instruments Optimize without measuring Medium
43 42 Accessibility Add accessibility labels Describe UI elements .accessibilityLabel for context Missing labels .accessibilityLabel("Close button") Button without label High https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(_:)-1d7jv
44 43 Accessibility Support Dynamic Type Respect text size preferences Scalable fonts and layouts Fixed font sizes .font(.body) with Dynamic Type .font(.system(size: 16)) High
45 44 Accessibility Use semantic views Proper accessibility traits Correct accessibilityTraits Wrong semantic meaning Button for actions Image for display Image that acts like button Medium
46 45 Testing Use ViewInspector for testing Third-party view testing ViewInspector for unit tests UI tests only ViewInspector assertions Only XCUITest Medium
47 46 Testing Test view models Unit test business logic XCTest for view model Skip view model testing Test ViewModel methods No unit tests Medium
48 47 Testing Use preview as visual test Previews catch visual regressions Multiple preview configurations No visual verification Preview different states Single preview only Low
49 48 Architecture Use MVVM pattern Separate view and logic ViewModel for business logic Logic in View ObservableObject ViewModel @State for complex logic Medium
50 49 Architecture Keep views dumb Views display view model state View reads from ViewModel Business logic in View view.items from vm.items Complex filtering in View Medium
51 50 Architecture Use dependency injection Inject dependencies for testing Initialize with dependencies Hard-coded dependencies init(service: ServiceProtocol) let service = RealService() Medium

View File

@@ -0,0 +1,54 @@
Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
Setup,CDN Version Lock,Always use Three.js r128 from cdnjs. It is the stable CDN baseline. Never use a floating 'latest' URL — it silently breaks when the CDN updates without warning.,Pin to the exact r128 cdnjs URL in every HTML file,Use unpkg@latest or any unversioned CDN URL that can silently update,"<script src=""https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js""></script>","<script src=""https://unpkg.com/three@latest""></script>",Critical,https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
Setup,CapsuleGeometry Does Not Exist in r128,THREE.CapsuleGeometry was introduced in r142. Using it on the r128 CDN throws 'CapsuleGeometry is not a constructor' and crashes the entire scene. Build a capsule from primitives instead.,Build a capsule from CylinderGeometry plus two SphereGeometry end caps,Call THREE.CapsuleGeometry on r128 — it is undefined and throws immediately,"const body = new THREE.Mesh(new THREE.CylinderGeometry(0.5, 0.5, 1, 16), mat); const topCap = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 8), mat); const botCap = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 8), mat); topCap.position.y = 0.5; botCap.position.y = -0.5; const group = new THREE.Group(); group.add(body, topCap, botCap);","const cap = new THREE.CapsuleGeometry(0.5, 1, 4, 8); // TypeError: CapsuleGeometry is not a constructor on r128",Critical,https://threejs.org/docs/#api/en/geometries/CapsuleGeometry
Setup,OrbitControls Must Be Loaded Separately,OrbitControls is NOT bundled in the core Three.js r128 CDN file. It lives in examples/js and must be loaded from a separate cdnjs script tag. THREE.OrbitControls is undefined without it.,Load the OrbitControls script from cdnjs examples path before your scene script,Expect THREE.OrbitControls to exist after loading only the core Three.js CDN script,"<!-- Load AFTER core Three.js script --> <script src=""https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/examples/js/controls/OrbitControls.min.js""></script>","<!-- Core only loaded — OrbitControls undefined --> <script src=""https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js""></script>",Critical,https://cdnjs.com/libraries/three.js/r128
Setup,Custom Drag Orbit Fallback,When OrbitControls cannot be loaded implement spherical orbit using mousedown/mousemove/mouseup. The key is rotating in spherical coordinates so both horizontal AND vertical drag work correctly.,Rotate camera in spherical coordinates so both axes respond correctly to drag,Move camera.position.x directly — vertical drag is silently ignored and the orbit is incorrect,"let dragging = false; let prev = { x: 0, y: 0 }; const radius = camera.position.length(); let theta = 0; let phi = Math.PI / 2; canvas.addEventListener('mousedown', () => dragging = true); canvas.addEventListener('mouseup', () => dragging = false); canvas.addEventListener('mousemove', e => { if (!dragging) return; theta -= (e.clientX - prev.x) * 0.005; phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi - (e.clientY - prev.y) * 0.005)); camera.position.set(radius * Math.sin(phi) * Math.sin(theta), radius * Math.cos(phi), radius * Math.sin(phi) * Math.cos(theta)); camera.lookAt(scene.position); prev = { x: e.clientX, y: e.clientY }; });","let dragging = false; let prev = { x: 0, y: 0 }; canvas.addEventListener('mousemove', e => { if (!dragging) return; camera.position.x += (e.clientX - prev.x) * 0.005; camera.lookAt(scene.position); prev = { x: e.clientX, y: e.clientY }; }); // BUG: Y-drag ignored; orbit is a horizontal slide not a sphere",High,https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
Setup,ESM vs CDN Import,When using a bundler import Three.js as an ES module. When using CDN the THREE global is already available — do not import it again. Mixing both loads Three.js twice and causes subtle runtime errors.,Match import style to build environment: ESM import for bundlers; rely on the window.THREE global for CDN pages,Mix a CDN script tag with an ES module import in the same file,"// Bundler project (Vite / webpack): import * as THREE from 'three'; // CDN project — no import needed; THREE is already global after the script tag","<!-- CDN script --> <script src=""r128.cdn""></script> <script type=""module""> import * as THREE from 'three'; // loads Three.js twice — version mismatch risk </script>",Critical,https://threejs.org/docs/#manual/en/introduction/Installation
Setup,Single Renderer Per Page,Create one WebGLRenderer instance for the lifetime of the page. Multiple renderers compete for the browser GPU context limit (816 contexts) and cause context-lost errors especially on mobile.,Reuse a single renderer and swap scene content instead of recreating the renderer,Create a new renderer on each component mount or scene transition,"const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(canvas.clientWidth, canvas.clientHeight); // renderer lives for the page lifetime","function showScene() { const renderer = new THREE.WebGLRenderer(); document.body.appendChild(renderer.domElement); } showScene(); showScene(); // two GPU contexts — crashes on mobile",Critical,https://threejs.org/docs/#api/en/renderers/WebGLRenderer
Setup,Pixel Ratio Cap at 2,Cap devicePixelRatio at 2. Retina displays report 3x or higher. Going from 2x to 3x multiplies pixel count by 2.25x with no visible quality improvement at normal viewing distance.,"Apply Math.min(window.devicePixelRatio, 2) — cap is at 2 not at 3",Pass window.devicePixelRatio directly without any cap,"renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));",renderer.setPixelRatio(window.devicePixelRatio); // 3x display = 9 pixels per CSS pixel = 2.25x GPU cost for zero quality gain,High,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setPixelRatio
Setup,Alpha Canvas Plus CSS Background,Set alpha:true on the renderer and control the background color through CSS rather than a renderer clear color. This composites the canvas correctly over any HTML content behind it.,Set alpha:true on renderer and let body or a parent div provide the background color,Set a solid renderer clear color when the canvas must composite over HTML behind it,"const renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setClearColor(0x000000, 0); // fully transparent canvas // body { background: #0d0d0d; } handles the visible color","renderer.setClearColor(0x111827); // fully opaque — HTML behind the canvas is blocked",Medium,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setClearColor
Camera,Aspect Ratio on Resize,Always update camera.aspect and call camera.updateProjectionMatrix() inside every resize handler. A stale aspect ratio causes the entire scene to appear stretched or squashed horizontally.,Update camera.aspect then call updateProjectionMatrix() on every resize,Let aspect ratio become stale after the browser window changes size,"window.addEventListener('resize', () => { camera.aspect = canvas.clientWidth / canvas.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(canvas.clientWidth, canvas.clientHeight); });","// No resize handler — scene stretches to fill a wider window without correcting the projection",High,https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
Camera,FOV Range 45 to 75,Use a field of view between 45 and 75 degrees. Below 45 creates compressed telephoto distortion. Above 90 creates visible fisheye distortion at frame edges.,Start at 75 for general interactive scenes; use 4555 for product close-ups,Use FOV above 90 or below 30 without a deliberate artistic reason,"const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000); // general const camera = new THREE.PerspectiveCamera(50, aspect, 0.1, 1000); // product shot","const camera = new THREE.PerspectiveCamera(120, aspect, 0.1, 1000); // fisheye distortion at all edges",Medium,https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
Camera,Explicit Position and lookAt,Always set an explicit camera position and call camera.lookAt() before the first render. The default camera at the origin pointing down -Z makes subjects at arbitrary coordinates invisible or tiny.,Set camera.position.set() and camera.lookAt() to frame the subject before the first render,Leave the camera at default position (0 0 0) with no lookAt — subject may be behind the camera or microscopic,"camera.position.set(0, 1.5, 5); camera.lookAt(new THREE.Vector3(0, 0, 0));","// No position or lookAt set — subject at y:2 is behind or above the default camera view",Medium,https://threejs.org/docs/#api/en/cameras/Camera.lookAt
Camera,OrbitControls vs GSAP Camera Rig,Use OrbitControls for model viewers and exploratory scenes where the user needs free-look. Use a GSAP scroll-driven camera rig for product reveals or storytelling where the camera path must stay fixed.,Match camera control approach to the UX intent of the scene,Use OrbitControls for a scripted reveal — users can orbit away from the reveal before it completes,"// Scroll storytelling — GSAP controls the path: gsap.to(camera.position, { z: 2, scrollTrigger: { trigger: '.scene', scrub: 1 } }); // Free-look model viewer — OrbitControls: const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; // call controls.update() in animate()","// Scripted product reveal: const controls = new THREE.OrbitControls(camera, renderer.domElement); // user can rotate away before the animation completes",High,https://threejs.org/docs/#examples/en/controls/OrbitControls
Geometry,Never Create Geometry Per Frame,Creating a new geometry inside animate() allocates a fresh GPU buffer every frame and exhausts VRAM within seconds. Create all geometry exactly once before the loop starts. Use attribute mutation if positions must change per frame.,Create all geometry before the animation loop; mutate BufferAttribute arrays in-place if needed,Call any new XxxGeometry() constructor inside the animation loop,"const geo = new THREE.SphereGeometry(1, 32, 32); // created once const mesh = new THREE.Mesh(geo, mat); scene.add(mesh); const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); mesh.rotation.y += clock.getDelta() * 0.8; // delta time renderer.render(scene, camera); }","function animate() { requestAnimationFrame(animate); const geo = new THREE.BoxGeometry(1, 1, 1); // NEW GPU buffer every frame — VRAM exhaustion }",Critical,https://threejs.org/docs/#api/en/core/BufferGeometry
Geometry,Share Geometry Across Meshes,When multiple objects share the same shape create one geometry instance and pass it to every Mesh. Each Mesh gets its own transform and material while all share a single GPU buffer.,Create one geometry and pass the same reference to every Mesh constructor,Create a separate identical geometry inside a loop for each object,"const geo = new THREE.BoxGeometry(1, 1, 1); // one GPU buffer for (let i = 0; i < 200; i++) { const m = new THREE.Mesh(geo, mat); m.position.set(Math.random() * 10, 0, Math.random() * 10); scene.add(m); }","for (let i = 0; i < 200; i++) { const geo = new THREE.BoxGeometry(1, 1, 1); // 200 separate GPU buffers scene.add(new THREE.Mesh(geo, mat)); }",Critical,https://threejs.org/docs/#api/en/core/BufferGeometry
Geometry,dispose on Scene Removal,Call geometry.dispose() and material.dispose() and texture.dispose() for every texture map when removing objects from the scene. Three.js never releases GPU resources automatically — they stay in VRAM until explicitly freed.,Dispose of geometry + material + every texture map before calling scene.remove(),Call scene.remove() alone without any dispose calls,"function removeMesh(mesh) { scene.remove(mesh); mesh.geometry.dispose(); if (mesh.material.map) mesh.material.map.dispose(); if (mesh.material.normalMap) mesh.material.normalMap.dispose(); mesh.material.dispose(); }","scene.remove(mesh); // geometry and all texture maps stay in GPU VRAM forever",Critical,https://threejs.org/docs/#api/en/core/BufferGeometry.dispose
Geometry,Segment Count Budget,Use the minimum segment count that achieves the desired silhouette quality. Hero objects: 3264 segments. Background objects: 816. Particle stand-ins: 68. High counts on background geometry waste GPU draw calls with zero visible benefit.,Apply a tiered segment budget based on the visual priority of each object in the scene,Default every sphere and cylinder to 64+ segments regardless of its role,"const bgSphere = new THREE.SphereGeometry(0.5, 8, 8); // background const heroSphere = new THREE.SphereGeometry(1, 64, 64); // foreground product","const particleSphere = new THREE.SphereGeometry(0.1, 64, 64); // 64 segments × 1000 particles = massive overdraw",Medium,https://threejs.org/docs/#api/en/geometries/SphereGeometry
Geometry,BufferGeometry for Custom Vertex Data,For any custom shape use BufferGeometry with setAttribute('position' ...) and a Float32Array. The legacy THREE.Geometry class was removed in r125 and throws ReferenceError in r128.,Use THREE.BufferGeometry with a Float32Array position attribute for custom vertex data,Reference or instantiate the removed THREE.Geometry class,"const geo = new THREE.BufferGeometry(); geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)); geo.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), 3));","const geo = new THREE.Geometry(); geo.vertices.push(new THREE.Vector3(0, 0, 0)); // ReferenceError: Geometry is not defined in r128",High,https://threejs.org/docs/#api/en/core/BufferGeometry
Materials,MeshBasicMaterial vs MeshStandardMaterial,MeshBasicMaterial ignores all lights and is significantly cheaper — use it for UI overlays HUDs and flat-colored decorative elements. MeshStandardMaterial is PBR-accurate and requires lights. Never use StandardMaterial where BasicMaterial suffices.,Use MeshBasicMaterial for any object that does not need lighting; use MeshStandardMaterial for physical objects,Apply MeshStandardMaterial to flat UI elements that never receive light — lights still run for them,"const uiMat = new THREE.MeshBasicMaterial({ color: 0xffffff }); // no lighting cost const physMat = new THREE.MeshStandardMaterial({ color: 0x4f46e5, roughness: 0.4, metalness: 0.6 });","const mat = new THREE.MeshStandardMaterial({ color: 0xffffff }); // on a 2D HUD card — lighting calculation runs with no visual benefit",Medium,https://threejs.org/docs/#api/en/materials/MeshStandardMaterial
Materials,Share Material Instances,Share one material instance across all meshes that have identical properties. Call mat.clone() only when individual meshes genuinely need different property values. Duplicate materials waste GPU VRAM.,Assign the same material reference to all meshes with identical visual properties,Create a new material inside a loop for objects that look identical,"const mat = new THREE.MeshStandardMaterial({ color: 0x4f46e5, roughness: 0.5 }); meshA.material = mat; meshB.material = mat; meshC.material = mat; // one GPU material","for (const m of meshes) { m.material = new THREE.MeshStandardMaterial({ color: 0x4f46e5 }); } // N redundant GPU materials",High,https://threejs.org/docs/#api/en/materials/Material
Materials,Dispose Textures Explicitly,Textures are the single largest consumer of GPU VRAM in most Three.js scenes. Call texture.dispose() when switching scenes or removing objects — Three.js does not garbage-collect GPU resources automatically.,Track all loaded textures and call dispose() on each one during scene teardown or on object removal,Load textures without any cleanup path — they persist in VRAM for the entire page lifetime,"const tex = new THREE.TextureLoader().load('img.jpg'); mesh.material.map = tex; // on teardown: tex.dispose(); mesh.material.dispose();","const tex = new THREE.TextureLoader().load('img.jpg'); scene.remove(mesh); // tex occupies GPU VRAM until page reload",High,https://threejs.org/docs/#api/en/textures/Texture.dispose
Lighting,Ambient Plus Directional Minimum,Any scene using MeshStandardMaterial or MeshPhongMaterial requires at minimum one AmbientLight (fill) and one DirectionalLight (shading direction). Without both the objects render as solid black — the material is there but no light reaches it.,Add AmbientLight for fill and DirectionalLight for shading whenever PBR or Phong materials are used,Use MeshStandardMaterial without adding any lights to the scene,"scene.add(new THREE.AmbientLight(0xffffff, 0.4)); const dirLight = new THREE.DirectionalLight(0xffffff, 1.0); dirLight.position.set(5, 10, 7.5); scene.add(dirLight);","const mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0x4f46e5 })); scene.add(mesh); // renders completely black — no lights in scene",Critical,https://threejs.org/docs/#api/en/lights/DirectionalLight
Lighting,Enable shadowMap Before castShadow,renderer.shadowMap.enabled = true must be set before any castShadow or receiveShadow flags. Without it the shadow map is never allocated and all shadow flags are silently ignored.,Set renderer.shadowMap.enabled = true first then set castShadow and receiveShadow on lights and meshes,Set castShadow on a light or mesh without enabling renderer.shadowMap.enabled — shadows never render,"renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; dirLight.castShadow = true; dirLight.shadow.mapSize.width = 2048; dirLight.shadow.mapSize.height = 2048; heroMesh.castShadow = true; ground.receiveShadow = true;","dirLight.castShadow = true; heroMesh.castShadow = true; // renderer.shadowMap.enabled never set — shadows silently do not render",High,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.shadowMap
Lighting,Selective Shadow Casting,Shadow map rendering redraws the entire scene from the light's perspective every frame. Enable castShadow only on the primary directional light and receiveShadow only on hero meshes and the ground plane.,Enable shadows only on the key light and the most important meshes,Enable castShadow and receiveShadow on every object in the scene including particles,"renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; dirLight.castShadow = true; heroMesh.castShadow = true; ground.receiveShadow = true; // particles and background meshes: no shadow flags","for (const m of allMeshes) { m.castShadow = true; m.receiveShadow = true; } // shadow map pass over particle system — expensive with no visible gain",High,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.shadowMap
Lighting,Skip Lights for MeshBasicMaterial,MeshBasicMaterial completely ignores all scene lights. Adding lights solely to illuminate BasicMaterial objects wastes a light pass on every frame with zero visible effect.,Omit lights entirely when every material in the scene is MeshBasicMaterial,Add AmbientLight and DirectionalLight to a scene that uses only MeshBasicMaterial,"// Scene uses only MeshBasicMaterial — no lights needed const mat = new THREE.MeshBasicMaterial({ color: 0x00ffff }); const mesh = new THREE.Mesh(geo, mat); scene.add(mesh); // MeshBasicMaterial is always fully lit by definition","scene.add(new THREE.AmbientLight(0xffffff, 1.0)); // wasted per-frame light pass — BasicMaterial ignores it entirely",Low,https://threejs.org/docs/#api/en/materials/MeshBasicMaterial
Raycasting,Single Shared Raycaster,Create exactly one Raycaster instance outside all event handlers. Store mouse coordinates in pointermove (cheap). Call setFromCamera and intersectObjects together inside the animate() loop — once per frame instead of once per mouse event.,Create one Raycaster; store mouse in pointermove; call setFromCamera + intersectObjects inside animate(),Create a new THREE.Raycaster() inside a mousemove handler or call setFromCamera inside the event listener,"const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); canvas.addEventListener('pointermove', e => { // only store coords — no raycasting here mouse.x = (e.clientX / canvas.clientWidth) * 2 - 1; mouse.y = -(e.clientY / canvas.clientHeight) * 2 + 1; }); // setFromCamera and intersectObjects run once per frame in animate()","window.addEventListener('mousemove', e => { const rc = new THREE.Raycaster(); // new allocation per event rc.setFromCamera(mouse, camera); rc.intersectObjects(targets, true); // fires 200+ times/sec });",Critical,https://threejs.org/docs/#api/en/core/Raycaster
Raycasting,NDC Mouse Coordinates,Raycasting requires mouse in Normalized Device Coordinates: X from -1 (left) to +1 (right) and Y from +1 (top) to -1 (bottom). The Y axis is inverted relative to screen space. A missing negation on Y causes all raycasts to miss or hit the wrong objects.,Apply the full NDC formula — including the negation on the Y axis,Forget to negate Y — raycasting appears to work but hits objects mirrored vertically,"mouse.x = (e.clientX / canvas.clientWidth) * 2 - 1; mouse.y = -(e.clientY / canvas.clientHeight) * 2 + 1; // Y is INVERTED","mouse.x = (e.clientX / canvas.clientWidth) * 2 - 1; mouse.y = (e.clientY / canvas.clientHeight) * 2 - 1; // BUG: Y not negated — raycasting is mirrored",Critical,https://threejs.org/docs/#api/en/core/Raycaster.setFromCamera
Raycasting,setFromCamera and intersectObjects in animate,Call raycaster.setFromCamera(mouse camera) and then raycaster.intersectObjects(targets true) inside the animate() loop. setFromCamera must come before intersectObjects every frame — without it the raycaster uses a stale ray direction.,Call setFromCamera then intersectObjects in order inside every animate() frame,Call intersectObjects without calling setFromCamera first — the raycaster uses a stale or zero ray,"function animate() { requestAnimationFrame(animate); raycaster.setFromCamera(mouse, camera); // update ray direction first const hits = raycaster.intersectObjects(targets, true); // then test intersections if (hits.length > 0) { document.body.style.cursor = 'pointer'; } else { document.body.style.cursor = 'auto'; } renderer.render(scene, camera); }","function animate() { requestAnimationFrame(animate); const hits = raycaster.intersectObjects(targets, true); // BUG: setFromCamera never called — stale ray — hits is always empty renderer.render(scene, camera); }",Critical,https://threejs.org/docs/#api/en/core/Raycaster
Raycasting,Recursive Flag for Groups and GLTF,Pass true as the second argument to intersectObjects when testing Groups or GLTF loaded models. Geometry lives on child Mesh objects — without recursive:true the parent group is tested but has no geometry and hits is always empty.,Use intersectObjects(targets true) for Groups or any loaded model,Raycast against a parent Group without the recursive flag,"const hits = raycaster.intersectObjects(scene.children, true); // catches all descendant meshes","const hits = raycaster.intersectObjects([modelGroup]); // recursive defaults to false — misses all children",High,https://threejs.org/docs/#api/en/core/Raycaster.intersectObjects
Raycasting,Cursor Feedback on Hover,Set document.body.style.cursor = 'pointer' when intersections are found and reset to 'auto' when none are found. Without cursor feedback users cannot discover that 3D objects are interactive.,Update cursor to pointer on hit; reset to auto on miss in the same animate loop block,Run raycasting and read hits without ever updating the cursor style,"if (hits.length > 0) { document.body.style.cursor = 'pointer'; } else { document.body.style.cursor = 'auto'; }","raycaster.setFromCamera(mouse, camera); raycaster.intersectObjects(targets, true); // hits ignored — cursor never changes — objects feel non-interactive",Medium,https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
Animation,requestAnimationFrame Loop Only,Drive the render loop exclusively with requestAnimationFrame or renderer.setAnimationLoop(). Never use setInterval or setTimeout — they are not synchronized to the display refresh rate and keep running when the tab is hidden draining battery.,Use requestAnimationFrame or renderer.setAnimationLoop() as the sole render loop driver,Use setInterval or setTimeout for render timing,"function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate();","setInterval(() => renderer.render(scene, camera), 16); // not display-synced; runs at full speed even when tab is hidden",Critical,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop
Animation,THREE.Clock for Delta Time,"Use THREE.Clock and clock.getDelta() for all time-based motion. A hardcoded increment like += 0.01 runs at 2x speed on 120Hz displays and at unpredictable speed when frames drop under load. CRITICAL: call getDelta() exactly ONCE per animate() frame and store the result in a local dt variable. getDelta() resets the internal clock on every call — a second call in the same frame always returns ~0, silently breaking any animation block that uses it.","Call clock.getDelta() once at the top of animate(); store result in dt; reuse dt everywhere in that frame","Call clock.getDelta() more than once per frame or inside a helper called from animate()","const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const dt = clock.getDelta(); // called ONCE — reuse dt below mesh.rotation.y += dt * 0.8; particles.rotation.y += dt * 0.1; // reuse dt, not a second getDelta() renderer.render(scene, camera); }","function animate() { requestAnimationFrame(animate); mesh.rotation.y += 0.01; // 0.01 rad/frame — runs 2x faster on 120Hz than on 60Hz }",High,https://threejs.org/docs/#api/en/core/Clock
Animation,Lerp for Smooth Pointer Follow,Use value += (target - value) * alpha each frame to smoothly interpolate toward a moving target. An alpha of 0.030.1 produces organic easing for camera follow pointer-tracking and hover scale effects without requiring GSAP.,Apply the lerp formula each frame with a small alpha for smooth organic motion,Snap a value directly to the target producing an instant jarring jump,"// In animate(): cameraTargetX = mouse.x * 3; camera.position.x += (cameraTargetX - camera.position.x) * 0.05; camera.position.y += (cameraTargetY - camera.position.y) * 0.05; camera.lookAt(scene.position);","// In animate(): camera.position.x = mouse.x * 3; // instant snap — jarring with no easing",Medium,https://threejs.org/docs/#api/en/math/MathUtils.lerp
Animation,GSAP for Multi-Step Sequences,Use GSAP timelines for any animation with more than two sequential steps or for scroll-linked camera paths. GSAP timelines can be paused reversed and scrubbed — far more maintainable than boolean state machines.,Use GSAP timelines for sequences with more than two steps and for scroll-driven animations,Implement multi-step sequences with boolean flags and manual frame counters,"const tl = gsap.timeline({ defaults: { ease: 'power2.out' } }); tl.from(mesh.position, { y: -3, duration: 1 }) .to(mesh.rotation, { y: Math.PI, duration: 1 }, '-=0.3') .to(camera.position, { z: 2, duration: 1.5 });","let step = 0; let t = 0; function animate() { if (step === 0 && (t += 0.01) >= 1) step = 1; } // grows unmanageable with 3+ steps",High,https://gsap.com/docs/v3/GSAP/Timeline/
Animation,Pause Render Loop on Tab Hidden,Use renderer.setAnimationLoop() as the loop driver so you can pass null to pause and a function to resume. Continuous rendering in a hidden tab wastes CPU GPU and battery with no user benefit.,Use renderer.setAnimationLoop(animate) to drive the loop; pass null to pause on visibilitychange,Drive with internal requestAnimationFrame and never stop the loop when the tab is hidden,"renderer.setAnimationLoop(animate); // use setAnimationLoop as the driver — not requestAnimationFrame inside animate function animate() { const dt = clock.getDelta(); renderer.render(scene, camera); } document.addEventListener('visibilitychange', () => { if (document.hidden) renderer.setAnimationLoop(null); else renderer.setAnimationLoop(animate); });","function animate() { requestAnimationFrame(animate); // self-referencing RAF cannot be stopped externally renderer.render(scene, camera); } animate(); // runs forever in background tab — drains battery",High,https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
GSAP,Load GSAP Before Scene Script,Load GSAP from its own CDN script tag before your scene script. In bundler projects install via npm and import. GSAP is a completely separate library from Three.js — never try to import it from the Three.js package.,Load GSAP CDN before the scene script; or npm install gsap and import separately,Import gsap from three or expect it to be defined without a separate load,"<!-- CDN: load GSAP before your scene script --> <script src=""https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js""></script> <!-- Bundler: --> // import gsap from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger';","import gsap from 'three'; // undefined — GSAP has nothing to do with Three.js",Critical,https://gsap.com/docs/v3/Installation
GSAP,Register ScrollTrigger Before Use,Call gsap.registerPlugin(ScrollTrigger) once at the top of your script before any scrollTrigger config object. Without registration the ScrollTrigger name is undefined and the tween throws immediately.,Call gsap.registerPlugin(ScrollTrigger) as the first line before any gsap.to/from/timeline with scrollTrigger,Include scrollTrigger config in gsap.to() calls without first registering the plugin,"gsap.registerPlugin(ScrollTrigger); gsap.to(camera.position, { z: 2, scrollTrigger: { trigger: '.hero-section', scrub: 1 } });","gsap.to(mesh.position, { scrollTrigger: { trigger: '.section', scrub: true } }); // TypeError: ScrollTrigger is not a constructor — not registered",Critical,https://gsap.com/docs/v3/Plugins/ScrollTrigger/
GSAP,Tween Three.js Properties Directly,GSAP can tween any numeric JavaScript property including mesh.position.x mesh.rotation.y and material.opacity. No wrapper or adaptor is needed. Note: to tween material.opacity the material must have transparent:true set before the tween starts.,Pass Three.js object properties directly to gsap.to(); set transparent:true before tweening opacity,Use a plain proxy object then manually copy values to Three.js properties every frame,"gsap.to(mesh.rotation, { y: Math.PI * 2, duration: 2, ease: 'power1.inOut' }); mesh.material.transparent = true; // required before tweening opacity gsap.to(mesh.material, { opacity: 0, duration: 1 });","const tw = { v: 0 }; gsap.to(tw, { v: Math.PI * 2, onUpdate: () => mesh.rotation.y = tw.v }); // unnecessary proxy wrapper",Medium,https://gsap.com/docs/v3/GSAP/gsap.to()
GSAP,scrub for Scroll-Driven Camera Path,Use scrub:true or scrub:1 to link camera movement continuously to scroll position as a 01 ratio. scrub:1 adds a 1-second lag for cinematic smoothness. onEnter/onLeave fire only once and create jarring snaps — not the right tool for a camera path.,Use scrub:1 for any scroll-controlled camera movement,Use onEnter or onLeave callbacks for camera motion — they snap instead of scrubbing,"gsap.registerPlugin(ScrollTrigger); gsap.to(camera.position, { x: 5, y: 2, z: 0, ease: 'none', scrollTrigger: { trigger: '.canvas-wrapper', start: 'top top', end: 'bottom bottom', scrub: 1 } });","gsap.to(camera.position, { z: 0, scrollTrigger: { trigger: '.section', onEnter: () => {} } }); // fires once at scroll threshold — not a continuous scrub",High,https://gsap.com/docs/v3/Plugins/ScrollTrigger/
Performance,InstancedMesh for Repeated Objects,Use THREE.InstancedMesh when rendering 50 or more identical objects. It submits all N transforms in one draw call instead of N draw calls and reduces CPU-GPU communication overhead dramatically.,Use InstancedMesh for any group of 50+ meshes sharing the same geometry and material,Create 50+ separate Mesh objects with the same geometry and material,"const COUNT = 500; const iMesh = new THREE.InstancedMesh(geo, mat, COUNT); const matrix = new THREE.Matrix4(); for (let i = 0; i < COUNT; i++) { matrix.setPosition(Math.random()*10, Math.random()*10, Math.random()*10); iMesh.setMatrixAt(i, matrix); } iMesh.instanceMatrix.needsUpdate = true; scene.add(iMesh);","for (let i = 0; i < 500; i++) { scene.add(new THREE.Mesh(geo, mat)); } // 500 separate draw calls per frame",High,https://threejs.org/docs/#api/en/objects/InstancedMesh
Performance,Tone Mapping and sRGB Encoding,Enable ACESFilmicToneMapping and sRGBEncoding on the renderer for accurate perceptual color. Without tone mapping colors appear washed out or over-saturated. These are renderer properties set after construction and take effect immediately.,Set renderer.toneMapping and renderer.outputEncoding after construction for all production scenes,Leave tone mapping and output encoding at defaults when the scene targets realistic visuals,"renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; renderer.outputEncoding = THREE.sRGBEncoding; // correct for r128","const renderer = new THREE.WebGLRenderer(); // defaults: NoToneMapping + LinearEncoding — colors appear flat and incorrect",Medium,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.toneMapping
Performance,antialias Set at Construction Only,The antialias option can only be set at WebGLRenderer construction time. Setting renderer.antialias after construction has absolutely no effect — the WebGL context is already created without it. Decide before instantiating.,Set antialias:true inside the WebGLRenderer constructor options object,Construct the renderer without antialias then try to enable it by assigning the property,"const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); // antialias baked into the WebGL context","const renderer = new THREE.WebGLRenderer(); renderer.antialias = true; // no effect — context created without AA — edges remain aliased",High,https://threejs.org/docs/#api/en/renderers/WebGLRenderer
Performance,FogExp2 for Depth and Far Culling,Use scene.fog to create atmospheric depth. As a secondary benefit objects that disappear into fog before the far plane stop contributing to draw calls — useful in scenes with large view distances.,Add FogExp2 to scenes with view distances above 100 units for both visual atmosphere and implicit far culling,Ignore fog in scenes with far:1000+ and many distant objects that contribute tiny pixels per draw call,"scene.fog = new THREE.FogExp2(0x0a0a0a, 0.02); // exponential — density feels more natural than linear","// far: 2000 with no fog — hundreds of distant objects too small to see still cost draw calls per frame",Low,https://threejs.org/docs/#api/en/scenes/FogExp2
Particles,BufferGeometry Plus Points for Particle Systems,Build all particle systems with BufferGeometry plus a Float32Array position attribute rendered as Points. Never use individual Mesh objects as particles — they cannot scale past a few hundred with good performance.,Use Points plus BufferGeometry for all particle effects,Create hundreds of individual Mesh objects to simulate a particle system,"const COUNT = 3000; const geo = new THREE.BufferGeometry(); const pos = new Float32Array(COUNT * 3); for (let i = 0; i < COUNT * 3; i++) pos[i] = (Math.random() - 0.5) * 20; geo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); const particles = new THREE.Points(geo, new THREE.PointsMaterial({ size: 0.05, color: 0xffffff })); scene.add(particles);","for (let i = 0; i < 500; i++) { scene.add(new THREE.Mesh(new THREE.SphereGeometry(0.05, 8, 8), mat)); } // 500 separate draw calls per frame",High,https://threejs.org/docs/#api/en/objects/Points
Particles,Particle Count Ceiling,Start particle systems at 10003000 particles. Beyond 50000 causes sustained frame drops on mid-range mobile. Always test on a real device before increasing the count — desktop and mobile GPU performance ratios can be 10:1.,Start at 3000 particles and profile on actual mobile hardware before raising the limit,Set particle count at 100000 or higher without any mobile profiling,"const COUNT = 3000; // safe mobile baseline — profile before going higher const pos = new Float32Array(COUNT * 3);","const COUNT = 150000; // 60fps on desktop — 8fps on a mid-range Android phone",High,https://threejs.org/docs/#api/en/objects/Points
Particles,needsUpdate After Buffer Mutation,After mutating any BufferAttribute array values per frame you must set geometry.attributes.position.needsUpdate = true so Three.js re-uploads the changed buffer to the GPU. Without it the GPU still uses the old data and particles appear completely frozen.,Set needsUpdate = true on the position attribute after every per-frame mutation of the array,Mutate the Float32Array values without flagging needsUpdate — positions update in JS but not on the GPU,"// In animate(): const pos = geo.attributes.position.array; for (let i = 0; i < pos.length; i += 3) { pos[i + 1] += Math.sin(clock.getElapsedTime() + i) * 0.001; // Y component } geo.attributes.position.needsUpdate = true; // GPU re-upload","// In animate(): pos[1] += 0.001; // JS array updated — GPU buffer is stale — particles do not move",Critical,https://threejs.org/docs/#api/en/core/BufferAttribute.needsUpdate
Responsive,Canvas Dimensions Not Window,Size the renderer and camera to the canvas element's clientWidth and clientHeight — not window.innerWidth and innerHeight. This is correct when the canvas is inside a flex or grid container that does not fill the full viewport.,Use canvas.clientWidth and canvas.clientHeight for all renderer and camera sizing,Hardcode renderer size to window.innerWidth/innerHeight when the canvas may be inside a container,"renderer.setSize(canvas.clientWidth, canvas.clientHeight); camera.aspect = canvas.clientWidth / canvas.clientHeight; camera.updateProjectionMatrix();","renderer.setSize(window.innerWidth, window.innerHeight); // wrong when canvas lives inside a sidebar or grid column",High,https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setSize
Responsive,ResizeObserver Over window resize Event,Use ResizeObserver on the canvas container instead of the window resize event. ResizeObserver fires when the container element changes size independently of the browser window — common in split-pane layouts and sidebar collapsing.,Attach ResizeObserver to the canvas parent element for accurate container-aware resize detection,Use only window.addEventListener('resize') for canvas sizing when the canvas is not fullscreen,"const ro = new ResizeObserver(entries => { const { width, height } = entries[0].contentRect; renderer.setSize(width, height); camera.aspect = width / height; camera.updateProjectionMatrix(); }); ro.observe(canvas.parentElement);","window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); }); // misses container-only resize events in split-pane UIs",Medium,https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
Responsive,Touch Events for Mobile Interaction,Add touchstart and touchmove listeners alongside mouse events so the scene remains interactive on mobile. Normalize touch coordinates to the same NDC range as mouse events and pass passive:false on touchmove if you call preventDefault.,Handle both mouse and touch input for any interactive 3D scene,Add only mouse event listeners and leave touch users with no interaction,"canvas.addEventListener('touchmove', e => { e.preventDefault(); const t = e.touches[0]; mouse.x = (t.clientX / canvas.clientWidth) * 2 - 1; mouse.y = -(t.clientY / canvas.clientHeight) * 2 + 1; }, { passive: false }); canvas.addEventListener('touchstart', e => { e.preventDefault(); }, { passive: false });","canvas.addEventListener('mousemove', handleMouse); // touch events unhandled — mobile users get no interaction",Medium,https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
Accessibility,prefers-reduced-motion,"Check window.matchMedia('(prefers-reduced-motion: reduce)') before starting any auto-rotation, particle animation, or camera movement. Users who enable this OS preference have motion sickness or vestibular disorders. IMPORTANT: reading .matches once at page load is a one-time snapshot — if the user changes their OS accessibility setting mid-session the scene will not react. Attach a 'change' listener to the MediaQueryList so noMotion stays in sync at runtime.","Use matchMedia.addEventListener('change') to keep noMotion reactive; gate all auto-animation on the live value","Read .matches once at startup and never update it — the scene ignores mid-session OS setting changes","const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); let noMotion = mq.matches; mq.addEventListener('change', e => { noMotion = e.matches; }); // In animate(): if (!noMotion) { mesh.rotation.y += dt * 0.8; particles.rotation.y += dt * 0.1; }","const noMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; // one-time snapshot — mid-session OS change is ignored entirely",High,https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
Accessibility,Canvas aria-label,Add role='img' and a descriptive aria-label to renderer.domElement after appending it to the DOM. Screen readers receive no information from a WebGL canvas — the aria-label is the only description they can announce to users.,Set role='img' and a meaningful aria-label on renderer.domElement before or after appending it,Append the canvas to the DOM with no accessibility attributes — invisible to screen readers,"renderer.domElement.setAttribute('role', 'img'); renderer.domElement.setAttribute('aria-label', 'Interactive 3D product viewer. Drag to rotate. Scroll to zoom.'); document.body.appendChild(renderer.domElement);","document.body.appendChild(renderer.domElement); // bare canvas — screen readers announce nothing",Medium,https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#accessibility_concerns
Production,Bundler Stack for Production,For production use Three.js via npm plus Vite. You get full tree-shaking reduced bundle size access to the complete examples/jsm library including OrbitControls GLTFLoader and EffectComposer and TypeScript support.,Use npm install three plus Vite or Webpack for any production client-facing project,Serve raw CDN script tags in a production application that needs tree-shaking or TypeScript,"npm install three gsap // then in your JS: import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';","<!-- In a Vite/React production build: --> <script src=""https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js""></script> // no tree-shaking — entire Three.js ships",Medium,https://threejs.org/docs/#manual/en/introduction/Installation
Production,GLTFLoader with scene traverse,Load 3D models using GLTFLoader and traverse gltf.scene to configure castShadow receiveShadow and material overrides on all child Mesh nodes. Calling scene.add(gltf.scene) alone silently skips all shadow and material configuration.,Use GLTFLoader and traverse the entire gltf.scene graph to set up shadows and materials on every Mesh child,Load a GLTF model and pass gltf.scene directly to scene.add without traversing child meshes,"import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; const loader = new GLTFLoader(); loader.load('model.glb', gltf => { gltf.scene.traverse(child => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); scene.add(gltf.scene); });","loader.load('model.glb', gltf => { scene.add(gltf.scene); // shadows and material setup silently skipped on all children });",Medium,https://threejs.org/docs/#examples/en/loaders/GLTFLoader
Production,LOD for Distance-Based Detail,Use THREE.LOD to automatically swap high-detail and low-detail geometry as objects move closer or farther from the camera. This maintains frame rate in scenes with many objects spread across a large depth range.,Use THREE.LOD to reduce triangle count on distant objects automatically,Render the same high-polygon geometry for every object regardless of its distance from the camera,"const lod = new THREE.LOD(); lod.addLevel(highDetailMesh, 0); // used when < 15 units away lod.addLevel(medDetailMesh, 15); // 1550 units lod.addLevel(lowDetailMesh, 50); // 50+ units scene.add(lod);","scene.add(highDetailMesh); // 64k-triangle mesh rendered at full cost whether 1 unit or 100 units from camera",Medium,https://threejs.org/docs/#api/en/objects/LOD
1 Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 Setup CDN Version Lock Always use Three.js r128 from cdnjs. It is the stable CDN baseline. Never use a floating 'latest' URL — it silently breaks when the CDN updates without warning. Pin to the exact r128 cdnjs URL in every HTML file Use unpkg@latest or any unversioned CDN URL that can silently update <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://unpkg.com/three@latest"></script> Critical https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
3 Setup CapsuleGeometry Does Not Exist in r128 THREE.CapsuleGeometry was introduced in r142. Using it on the r128 CDN throws 'CapsuleGeometry is not a constructor' and crashes the entire scene. Build a capsule from primitives instead. Build a capsule from CylinderGeometry plus two SphereGeometry end caps Call THREE.CapsuleGeometry on r128 — it is undefined and throws immediately const body = new THREE.Mesh(new THREE.CylinderGeometry(0.5, 0.5, 1, 16), mat); const topCap = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 8), mat); const botCap = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 8), mat); topCap.position.y = 0.5; botCap.position.y = -0.5; const group = new THREE.Group(); group.add(body, topCap, botCap); const cap = new THREE.CapsuleGeometry(0.5, 1, 4, 8); // TypeError: CapsuleGeometry is not a constructor on r128 Critical https://threejs.org/docs/#api/en/geometries/CapsuleGeometry
4 Setup OrbitControls Must Be Loaded Separately OrbitControls is NOT bundled in the core Three.js r128 CDN file. It lives in examples/js and must be loaded from a separate cdnjs script tag. THREE.OrbitControls is undefined without it. Load the OrbitControls script from cdnjs examples path before your scene script Expect THREE.OrbitControls to exist after loading only the core Three.js CDN script <!-- Load AFTER core Three.js script --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/examples/js/controls/OrbitControls.min.js"></script> <!-- Core only loaded — OrbitControls undefined --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> Critical https://cdnjs.com/libraries/three.js/r128
5 Setup Custom Drag Orbit Fallback When OrbitControls cannot be loaded implement spherical orbit using mousedown/mousemove/mouseup. The key is rotating in spherical coordinates so both horizontal AND vertical drag work correctly. Rotate camera in spherical coordinates so both axes respond correctly to drag Move camera.position.x directly — vertical drag is silently ignored and the orbit is incorrect let dragging = false; let prev = { x: 0, y: 0 }; const radius = camera.position.length(); let theta = 0; let phi = Math.PI / 2; canvas.addEventListener('mousedown', () => dragging = true); canvas.addEventListener('mouseup', () => dragging = false); canvas.addEventListener('mousemove', e => { if (!dragging) return; theta -= (e.clientX - prev.x) * 0.005; phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi - (e.clientY - prev.y) * 0.005)); camera.position.set(radius * Math.sin(phi) * Math.sin(theta), radius * Math.cos(phi), radius * Math.sin(phi) * Math.cos(theta)); camera.lookAt(scene.position); prev = { x: e.clientX, y: e.clientY }; }); let dragging = false; let prev = { x: 0, y: 0 }; canvas.addEventListener('mousemove', e => { if (!dragging) return; camera.position.x += (e.clientX - prev.x) * 0.005; camera.lookAt(scene.position); prev = { x: e.clientX, y: e.clientY }; }); // BUG: Y-drag ignored; orbit is a horizontal slide not a sphere High https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
6 Setup ESM vs CDN Import When using a bundler import Three.js as an ES module. When using CDN the THREE global is already available — do not import it again. Mixing both loads Three.js twice and causes subtle runtime errors. Match import style to build environment: ESM import for bundlers; rely on the window.THREE global for CDN pages Mix a CDN script tag with an ES module import in the same file // Bundler project (Vite / webpack): import * as THREE from 'three'; // CDN project — no import needed; THREE is already global after the script tag <!-- CDN script --> <script src="r128.cdn"></script> <script type="module"> import * as THREE from 'three'; // loads Three.js twice — version mismatch risk </script> Critical https://threejs.org/docs/#manual/en/introduction/Installation
7 Setup Single Renderer Per Page Create one WebGLRenderer instance for the lifetime of the page. Multiple renderers compete for the browser GPU context limit (8–16 contexts) and cause context-lost errors especially on mobile. Reuse a single renderer and swap scene content instead of recreating the renderer Create a new renderer on each component mount or scene transition const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(canvas.clientWidth, canvas.clientHeight); // renderer lives for the page lifetime function showScene() { const renderer = new THREE.WebGLRenderer(); document.body.appendChild(renderer.domElement); } showScene(); showScene(); // two GPU contexts — crashes on mobile Critical https://threejs.org/docs/#api/en/renderers/WebGLRenderer
8 Setup Pixel Ratio Cap at 2 Cap devicePixelRatio at 2. Retina displays report 3x or higher. Going from 2x to 3x multiplies pixel count by 2.25x with no visible quality improvement at normal viewing distance. Apply Math.min(window.devicePixelRatio, 2) — cap is at 2 not at 3 Pass window.devicePixelRatio directly without any cap renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setPixelRatio(window.devicePixelRatio); // 3x display = 9 pixels per CSS pixel = 2.25x GPU cost for zero quality gain High https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setPixelRatio
9 Setup Alpha Canvas Plus CSS Background Set alpha:true on the renderer and control the background color through CSS rather than a renderer clear color. This composites the canvas correctly over any HTML content behind it. Set alpha:true on renderer and let body or a parent div provide the background color Set a solid renderer clear color when the canvas must composite over HTML behind it const renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setClearColor(0x000000, 0); // fully transparent canvas // body { background: #0d0d0d; } handles the visible color renderer.setClearColor(0x111827); // fully opaque — HTML behind the canvas is blocked Medium https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setClearColor
10 Camera Aspect Ratio on Resize Always update camera.aspect and call camera.updateProjectionMatrix() inside every resize handler. A stale aspect ratio causes the entire scene to appear stretched or squashed horizontally. Update camera.aspect then call updateProjectionMatrix() on every resize Let aspect ratio become stale after the browser window changes size window.addEventListener('resize', () => { camera.aspect = canvas.clientWidth / canvas.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(canvas.clientWidth, canvas.clientHeight); }); // No resize handler — scene stretches to fill a wider window without correcting the projection High https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
11 Camera FOV Range 45 to 75 Use a field of view between 45 and 75 degrees. Below 45 creates compressed telephoto distortion. Above 90 creates visible fisheye distortion at frame edges. Start at 75 for general interactive scenes; use 45–55 for product close-ups Use FOV above 90 or below 30 without a deliberate artistic reason const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000); // general const camera = new THREE.PerspectiveCamera(50, aspect, 0.1, 1000); // product shot const camera = new THREE.PerspectiveCamera(120, aspect, 0.1, 1000); // fisheye distortion at all edges Medium https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
12 Camera Explicit Position and lookAt Always set an explicit camera position and call camera.lookAt() before the first render. The default camera at the origin pointing down -Z makes subjects at arbitrary coordinates invisible or tiny. Set camera.position.set() and camera.lookAt() to frame the subject before the first render Leave the camera at default position (0 0 0) with no lookAt — subject may be behind the camera or microscopic camera.position.set(0, 1.5, 5); camera.lookAt(new THREE.Vector3(0, 0, 0)); // No position or lookAt set — subject at y:2 is behind or above the default camera view Medium https://threejs.org/docs/#api/en/cameras/Camera.lookAt
13 Camera OrbitControls vs GSAP Camera Rig Use OrbitControls for model viewers and exploratory scenes where the user needs free-look. Use a GSAP scroll-driven camera rig for product reveals or storytelling where the camera path must stay fixed. Match camera control approach to the UX intent of the scene Use OrbitControls for a scripted reveal — users can orbit away from the reveal before it completes // Scroll storytelling — GSAP controls the path: gsap.to(camera.position, { z: 2, scrollTrigger: { trigger: '.scene', scrub: 1 } }); // Free-look model viewer — OrbitControls: const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; // call controls.update() in animate() // Scripted product reveal: const controls = new THREE.OrbitControls(camera, renderer.domElement); // user can rotate away before the animation completes High https://threejs.org/docs/#examples/en/controls/OrbitControls
14 Geometry Never Create Geometry Per Frame Creating a new geometry inside animate() allocates a fresh GPU buffer every frame and exhausts VRAM within seconds. Create all geometry exactly once before the loop starts. Use attribute mutation if positions must change per frame. Create all geometry before the animation loop; mutate BufferAttribute arrays in-place if needed Call any new XxxGeometry() constructor inside the animation loop const geo = new THREE.SphereGeometry(1, 32, 32); // created once const mesh = new THREE.Mesh(geo, mat); scene.add(mesh); const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); mesh.rotation.y += clock.getDelta() * 0.8; // delta time renderer.render(scene, camera); } function animate() { requestAnimationFrame(animate); const geo = new THREE.BoxGeometry(1, 1, 1); // NEW GPU buffer every frame — VRAM exhaustion } Critical https://threejs.org/docs/#api/en/core/BufferGeometry
15 Geometry Share Geometry Across Meshes When multiple objects share the same shape create one geometry instance and pass it to every Mesh. Each Mesh gets its own transform and material while all share a single GPU buffer. Create one geometry and pass the same reference to every Mesh constructor Create a separate identical geometry inside a loop for each object const geo = new THREE.BoxGeometry(1, 1, 1); // one GPU buffer for (let i = 0; i < 200; i++) { const m = new THREE.Mesh(geo, mat); m.position.set(Math.random() * 10, 0, Math.random() * 10); scene.add(m); } for (let i = 0; i < 200; i++) { const geo = new THREE.BoxGeometry(1, 1, 1); // 200 separate GPU buffers scene.add(new THREE.Mesh(geo, mat)); } Critical https://threejs.org/docs/#api/en/core/BufferGeometry
16 Geometry dispose on Scene Removal Call geometry.dispose() and material.dispose() and texture.dispose() for every texture map when removing objects from the scene. Three.js never releases GPU resources automatically — they stay in VRAM until explicitly freed. Dispose of geometry + material + every texture map before calling scene.remove() Call scene.remove() alone without any dispose calls function removeMesh(mesh) { scene.remove(mesh); mesh.geometry.dispose(); if (mesh.material.map) mesh.material.map.dispose(); if (mesh.material.normalMap) mesh.material.normalMap.dispose(); mesh.material.dispose(); } scene.remove(mesh); // geometry and all texture maps stay in GPU VRAM forever Critical https://threejs.org/docs/#api/en/core/BufferGeometry.dispose
17 Geometry Segment Count Budget Use the minimum segment count that achieves the desired silhouette quality. Hero objects: 32–64 segments. Background objects: 8–16. Particle stand-ins: 6–8. High counts on background geometry waste GPU draw calls with zero visible benefit. Apply a tiered segment budget based on the visual priority of each object in the scene Default every sphere and cylinder to 64+ segments regardless of its role const bgSphere = new THREE.SphereGeometry(0.5, 8, 8); // background const heroSphere = new THREE.SphereGeometry(1, 64, 64); // foreground product const particleSphere = new THREE.SphereGeometry(0.1, 64, 64); // 64 segments × 1000 particles = massive overdraw Medium https://threejs.org/docs/#api/en/geometries/SphereGeometry
18 Geometry BufferGeometry for Custom Vertex Data For any custom shape use BufferGeometry with setAttribute('position' ...) and a Float32Array. The legacy THREE.Geometry class was removed in r125 and throws ReferenceError in r128. Use THREE.BufferGeometry with a Float32Array position attribute for custom vertex data Reference or instantiate the removed THREE.Geometry class const geo = new THREE.BufferGeometry(); geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)); geo.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), 3)); const geo = new THREE.Geometry(); geo.vertices.push(new THREE.Vector3(0, 0, 0)); // ReferenceError: Geometry is not defined in r128 High https://threejs.org/docs/#api/en/core/BufferGeometry
19 Materials MeshBasicMaterial vs MeshStandardMaterial MeshBasicMaterial ignores all lights and is significantly cheaper — use it for UI overlays HUDs and flat-colored decorative elements. MeshStandardMaterial is PBR-accurate and requires lights. Never use StandardMaterial where BasicMaterial suffices. Use MeshBasicMaterial for any object that does not need lighting; use MeshStandardMaterial for physical objects Apply MeshStandardMaterial to flat UI elements that never receive light — lights still run for them const uiMat = new THREE.MeshBasicMaterial({ color: 0xffffff }); // no lighting cost const physMat = new THREE.MeshStandardMaterial({ color: 0x4f46e5, roughness: 0.4, metalness: 0.6 }); const mat = new THREE.MeshStandardMaterial({ color: 0xffffff }); // on a 2D HUD card — lighting calculation runs with no visual benefit Medium https://threejs.org/docs/#api/en/materials/MeshStandardMaterial
20 Materials Share Material Instances Share one material instance across all meshes that have identical properties. Call mat.clone() only when individual meshes genuinely need different property values. Duplicate materials waste GPU VRAM. Assign the same material reference to all meshes with identical visual properties Create a new material inside a loop for objects that look identical const mat = new THREE.MeshStandardMaterial({ color: 0x4f46e5, roughness: 0.5 }); meshA.material = mat; meshB.material = mat; meshC.material = mat; // one GPU material for (const m of meshes) { m.material = new THREE.MeshStandardMaterial({ color: 0x4f46e5 }); } // N redundant GPU materials High https://threejs.org/docs/#api/en/materials/Material
21 Materials Dispose Textures Explicitly Textures are the single largest consumer of GPU VRAM in most Three.js scenes. Call texture.dispose() when switching scenes or removing objects — Three.js does not garbage-collect GPU resources automatically. Track all loaded textures and call dispose() on each one during scene teardown or on object removal Load textures without any cleanup path — they persist in VRAM for the entire page lifetime const tex = new THREE.TextureLoader().load('img.jpg'); mesh.material.map = tex; // on teardown: tex.dispose(); mesh.material.dispose(); const tex = new THREE.TextureLoader().load('img.jpg'); scene.remove(mesh); // tex occupies GPU VRAM until page reload High https://threejs.org/docs/#api/en/textures/Texture.dispose
22 Lighting Ambient Plus Directional Minimum Any scene using MeshStandardMaterial or MeshPhongMaterial requires at minimum one AmbientLight (fill) and one DirectionalLight (shading direction). Without both the objects render as solid black — the material is there but no light reaches it. Add AmbientLight for fill and DirectionalLight for shading whenever PBR or Phong materials are used Use MeshStandardMaterial without adding any lights to the scene scene.add(new THREE.AmbientLight(0xffffff, 0.4)); const dirLight = new THREE.DirectionalLight(0xffffff, 1.0); dirLight.position.set(5, 10, 7.5); scene.add(dirLight); const mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0x4f46e5 })); scene.add(mesh); // renders completely black — no lights in scene Critical https://threejs.org/docs/#api/en/lights/DirectionalLight
23 Lighting Enable shadowMap Before castShadow renderer.shadowMap.enabled = true must be set before any castShadow or receiveShadow flags. Without it the shadow map is never allocated and all shadow flags are silently ignored. Set renderer.shadowMap.enabled = true first then set castShadow and receiveShadow on lights and meshes Set castShadow on a light or mesh without enabling renderer.shadowMap.enabled — shadows never render renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; dirLight.castShadow = true; dirLight.shadow.mapSize.width = 2048; dirLight.shadow.mapSize.height = 2048; heroMesh.castShadow = true; ground.receiveShadow = true; dirLight.castShadow = true; heroMesh.castShadow = true; // renderer.shadowMap.enabled never set — shadows silently do not render High https://threejs.org/docs/#api/en/renderers/WebGLRenderer.shadowMap
24 Lighting Selective Shadow Casting Shadow map rendering redraws the entire scene from the light's perspective every frame. Enable castShadow only on the primary directional light and receiveShadow only on hero meshes and the ground plane. Enable shadows only on the key light and the most important meshes Enable castShadow and receiveShadow on every object in the scene including particles renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; dirLight.castShadow = true; heroMesh.castShadow = true; ground.receiveShadow = true; // particles and background meshes: no shadow flags for (const m of allMeshes) { m.castShadow = true; m.receiveShadow = true; } // shadow map pass over particle system — expensive with no visible gain High https://threejs.org/docs/#api/en/renderers/WebGLRenderer.shadowMap
25 Lighting Skip Lights for MeshBasicMaterial MeshBasicMaterial completely ignores all scene lights. Adding lights solely to illuminate BasicMaterial objects wastes a light pass on every frame with zero visible effect. Omit lights entirely when every material in the scene is MeshBasicMaterial Add AmbientLight and DirectionalLight to a scene that uses only MeshBasicMaterial // Scene uses only MeshBasicMaterial — no lights needed const mat = new THREE.MeshBasicMaterial({ color: 0x00ffff }); const mesh = new THREE.Mesh(geo, mat); scene.add(mesh); // MeshBasicMaterial is always fully lit by definition scene.add(new THREE.AmbientLight(0xffffff, 1.0)); // wasted per-frame light pass — BasicMaterial ignores it entirely Low https://threejs.org/docs/#api/en/materials/MeshBasicMaterial
26 Raycasting Single Shared Raycaster Create exactly one Raycaster instance outside all event handlers. Store mouse coordinates in pointermove (cheap). Call setFromCamera and intersectObjects together inside the animate() loop — once per frame instead of once per mouse event. Create one Raycaster; store mouse in pointermove; call setFromCamera + intersectObjects inside animate() Create a new THREE.Raycaster() inside a mousemove handler or call setFromCamera inside the event listener const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); canvas.addEventListener('pointermove', e => { // only store coords — no raycasting here mouse.x = (e.clientX / canvas.clientWidth) * 2 - 1; mouse.y = -(e.clientY / canvas.clientHeight) * 2 + 1; }); // setFromCamera and intersectObjects run once per frame in animate() window.addEventListener('mousemove', e => { const rc = new THREE.Raycaster(); // new allocation per event rc.setFromCamera(mouse, camera); rc.intersectObjects(targets, true); // fires 200+ times/sec }); Critical https://threejs.org/docs/#api/en/core/Raycaster
27 Raycasting NDC Mouse Coordinates Raycasting requires mouse in Normalized Device Coordinates: X from -1 (left) to +1 (right) and Y from +1 (top) to -1 (bottom). The Y axis is inverted relative to screen space. A missing negation on Y causes all raycasts to miss or hit the wrong objects. Apply the full NDC formula — including the negation on the Y axis Forget to negate Y — raycasting appears to work but hits objects mirrored vertically mouse.x = (e.clientX / canvas.clientWidth) * 2 - 1; mouse.y = -(e.clientY / canvas.clientHeight) * 2 + 1; // Y is INVERTED mouse.x = (e.clientX / canvas.clientWidth) * 2 - 1; mouse.y = (e.clientY / canvas.clientHeight) * 2 - 1; // BUG: Y not negated — raycasting is mirrored Critical https://threejs.org/docs/#api/en/core/Raycaster.setFromCamera
28 Raycasting setFromCamera and intersectObjects in animate Call raycaster.setFromCamera(mouse camera) and then raycaster.intersectObjects(targets true) inside the animate() loop. setFromCamera must come before intersectObjects every frame — without it the raycaster uses a stale ray direction. Call setFromCamera then intersectObjects in order inside every animate() frame Call intersectObjects without calling setFromCamera first — the raycaster uses a stale or zero ray function animate() { requestAnimationFrame(animate); raycaster.setFromCamera(mouse, camera); // update ray direction first const hits = raycaster.intersectObjects(targets, true); // then test intersections if (hits.length > 0) { document.body.style.cursor = 'pointer'; } else { document.body.style.cursor = 'auto'; } renderer.render(scene, camera); } function animate() { requestAnimationFrame(animate); const hits = raycaster.intersectObjects(targets, true); // BUG: setFromCamera never called — stale ray — hits is always empty renderer.render(scene, camera); } Critical https://threejs.org/docs/#api/en/core/Raycaster
29 Raycasting Recursive Flag for Groups and GLTF Pass true as the second argument to intersectObjects when testing Groups or GLTF loaded models. Geometry lives on child Mesh objects — without recursive:true the parent group is tested but has no geometry and hits is always empty. Use intersectObjects(targets true) for Groups or any loaded model Raycast against a parent Group without the recursive flag const hits = raycaster.intersectObjects(scene.children, true); // catches all descendant meshes const hits = raycaster.intersectObjects([modelGroup]); // recursive defaults to false — misses all children High https://threejs.org/docs/#api/en/core/Raycaster.intersectObjects
30 Raycasting Cursor Feedback on Hover Set document.body.style.cursor = 'pointer' when intersections are found and reset to 'auto' when none are found. Without cursor feedback users cannot discover that 3D objects are interactive. Update cursor to pointer on hit; reset to auto on miss in the same animate loop block Run raycasting and read hits without ever updating the cursor style if (hits.length > 0) { document.body.style.cursor = 'pointer'; } else { document.body.style.cursor = 'auto'; } raycaster.setFromCamera(mouse, camera); raycaster.intersectObjects(targets, true); // hits ignored — cursor never changes — objects feel non-interactive Medium https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
31 Animation requestAnimationFrame Loop Only Drive the render loop exclusively with requestAnimationFrame or renderer.setAnimationLoop(). Never use setInterval or setTimeout — they are not synchronized to the display refresh rate and keep running when the tab is hidden draining battery. Use requestAnimationFrame or renderer.setAnimationLoop() as the sole render loop driver Use setInterval or setTimeout for render timing function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); setInterval(() => renderer.render(scene, camera), 16); // not display-synced; runs at full speed even when tab is hidden Critical https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop
32 Animation THREE.Clock for Delta Time Use THREE.Clock and clock.getDelta() for all time-based motion. A hardcoded increment like += 0.01 runs at 2x speed on 120Hz displays and at unpredictable speed when frames drop under load. CRITICAL: call getDelta() exactly ONCE per animate() frame and store the result in a local dt variable. getDelta() resets the internal clock on every call — a second call in the same frame always returns ~0, silently breaking any animation block that uses it. Call clock.getDelta() once at the top of animate(); store result in dt; reuse dt everywhere in that frame Call clock.getDelta() more than once per frame or inside a helper called from animate() const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const dt = clock.getDelta(); // called ONCE — reuse dt below mesh.rotation.y += dt * 0.8; particles.rotation.y += dt * 0.1; // reuse dt, not a second getDelta() renderer.render(scene, camera); } function animate() { requestAnimationFrame(animate); mesh.rotation.y += 0.01; // 0.01 rad/frame — runs 2x faster on 120Hz than on 60Hz } High https://threejs.org/docs/#api/en/core/Clock
33 Animation Lerp for Smooth Pointer Follow Use value += (target - value) * alpha each frame to smoothly interpolate toward a moving target. An alpha of 0.03–0.1 produces organic easing for camera follow pointer-tracking and hover scale effects without requiring GSAP. Apply the lerp formula each frame with a small alpha for smooth organic motion Snap a value directly to the target producing an instant jarring jump // In animate(): cameraTargetX = mouse.x * 3; camera.position.x += (cameraTargetX - camera.position.x) * 0.05; camera.position.y += (cameraTargetY - camera.position.y) * 0.05; camera.lookAt(scene.position); // In animate(): camera.position.x = mouse.x * 3; // instant snap — jarring with no easing Medium https://threejs.org/docs/#api/en/math/MathUtils.lerp
34 Animation GSAP for Multi-Step Sequences Use GSAP timelines for any animation with more than two sequential steps or for scroll-linked camera paths. GSAP timelines can be paused reversed and scrubbed — far more maintainable than boolean state machines. Use GSAP timelines for sequences with more than two steps and for scroll-driven animations Implement multi-step sequences with boolean flags and manual frame counters const tl = gsap.timeline({ defaults: { ease: 'power2.out' } }); tl.from(mesh.position, { y: -3, duration: 1 }) .to(mesh.rotation, { y: Math.PI, duration: 1 }, '-=0.3') .to(camera.position, { z: 2, duration: 1.5 }); let step = 0; let t = 0; function animate() { if (step === 0 && (t += 0.01) >= 1) step = 1; } // grows unmanageable with 3+ steps High https://gsap.com/docs/v3/GSAP/Timeline/
35 Animation Pause Render Loop on Tab Hidden Use renderer.setAnimationLoop() as the loop driver so you can pass null to pause and a function to resume. Continuous rendering in a hidden tab wastes CPU GPU and battery with no user benefit. Use renderer.setAnimationLoop(animate) to drive the loop; pass null to pause on visibilitychange Drive with internal requestAnimationFrame and never stop the loop when the tab is hidden renderer.setAnimationLoop(animate); // use setAnimationLoop as the driver — not requestAnimationFrame inside animate function animate() { const dt = clock.getDelta(); renderer.render(scene, camera); } document.addEventListener('visibilitychange', () => { if (document.hidden) renderer.setAnimationLoop(null); else renderer.setAnimationLoop(animate); }); function animate() { requestAnimationFrame(animate); // self-referencing RAF cannot be stopped externally renderer.render(scene, camera); } animate(); // runs forever in background tab — drains battery High https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
36 GSAP Load GSAP Before Scene Script Load GSAP from its own CDN script tag before your scene script. In bundler projects install via npm and import. GSAP is a completely separate library from Three.js — never try to import it from the Three.js package. Load GSAP CDN before the scene script; or npm install gsap and import separately Import gsap from three or expect it to be defined without a separate load <!-- CDN: load GSAP before your scene script --> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> <!-- Bundler: --> // import gsap from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; import gsap from 'three'; // undefined — GSAP has nothing to do with Three.js Critical https://gsap.com/docs/v3/Installation
37 GSAP Register ScrollTrigger Before Use Call gsap.registerPlugin(ScrollTrigger) once at the top of your script before any scrollTrigger config object. Without registration the ScrollTrigger name is undefined and the tween throws immediately. Call gsap.registerPlugin(ScrollTrigger) as the first line before any gsap.to/from/timeline with scrollTrigger Include scrollTrigger config in gsap.to() calls without first registering the plugin gsap.registerPlugin(ScrollTrigger); gsap.to(camera.position, { z: 2, scrollTrigger: { trigger: '.hero-section', scrub: 1 } }); gsap.to(mesh.position, { scrollTrigger: { trigger: '.section', scrub: true } }); // TypeError: ScrollTrigger is not a constructor — not registered Critical https://gsap.com/docs/v3/Plugins/ScrollTrigger/
38 GSAP Tween Three.js Properties Directly GSAP can tween any numeric JavaScript property including mesh.position.x mesh.rotation.y and material.opacity. No wrapper or adaptor is needed. Note: to tween material.opacity the material must have transparent:true set before the tween starts. Pass Three.js object properties directly to gsap.to(); set transparent:true before tweening opacity Use a plain proxy object then manually copy values to Three.js properties every frame gsap.to(mesh.rotation, { y: Math.PI * 2, duration: 2, ease: 'power1.inOut' }); mesh.material.transparent = true; // required before tweening opacity gsap.to(mesh.material, { opacity: 0, duration: 1 }); const tw = { v: 0 }; gsap.to(tw, { v: Math.PI * 2, onUpdate: () => mesh.rotation.y = tw.v }); // unnecessary proxy wrapper Medium https://gsap.com/docs/v3/GSAP/gsap.to()
39 GSAP scrub for Scroll-Driven Camera Path Use scrub:true or scrub:1 to link camera movement continuously to scroll position as a 0–1 ratio. scrub:1 adds a 1-second lag for cinematic smoothness. onEnter/onLeave fire only once and create jarring snaps — not the right tool for a camera path. Use scrub:1 for any scroll-controlled camera movement Use onEnter or onLeave callbacks for camera motion — they snap instead of scrubbing gsap.registerPlugin(ScrollTrigger); gsap.to(camera.position, { x: 5, y: 2, z: 0, ease: 'none', scrollTrigger: { trigger: '.canvas-wrapper', start: 'top top', end: 'bottom bottom', scrub: 1 } }); gsap.to(camera.position, { z: 0, scrollTrigger: { trigger: '.section', onEnter: () => {} } }); // fires once at scroll threshold — not a continuous scrub High https://gsap.com/docs/v3/Plugins/ScrollTrigger/
40 Performance InstancedMesh for Repeated Objects Use THREE.InstancedMesh when rendering 50 or more identical objects. It submits all N transforms in one draw call instead of N draw calls and reduces CPU-GPU communication overhead dramatically. Use InstancedMesh for any group of 50+ meshes sharing the same geometry and material Create 50+ separate Mesh objects with the same geometry and material const COUNT = 500; const iMesh = new THREE.InstancedMesh(geo, mat, COUNT); const matrix = new THREE.Matrix4(); for (let i = 0; i < COUNT; i++) { matrix.setPosition(Math.random()*10, Math.random()*10, Math.random()*10); iMesh.setMatrixAt(i, matrix); } iMesh.instanceMatrix.needsUpdate = true; scene.add(iMesh); for (let i = 0; i < 500; i++) { scene.add(new THREE.Mesh(geo, mat)); } // 500 separate draw calls per frame High https://threejs.org/docs/#api/en/objects/InstancedMesh
41 Performance Tone Mapping and sRGB Encoding Enable ACESFilmicToneMapping and sRGBEncoding on the renderer for accurate perceptual color. Without tone mapping colors appear washed out or over-saturated. These are renderer properties set after construction and take effect immediately. Set renderer.toneMapping and renderer.outputEncoding after construction for all production scenes Leave tone mapping and output encoding at defaults when the scene targets realistic visuals renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; renderer.outputEncoding = THREE.sRGBEncoding; // correct for r128 const renderer = new THREE.WebGLRenderer(); // defaults: NoToneMapping + LinearEncoding — colors appear flat and incorrect Medium https://threejs.org/docs/#api/en/renderers/WebGLRenderer.toneMapping
42 Performance antialias Set at Construction Only The antialias option can only be set at WebGLRenderer construction time. Setting renderer.antialias after construction has absolutely no effect — the WebGL context is already created without it. Decide before instantiating. Set antialias:true inside the WebGLRenderer constructor options object Construct the renderer without antialias then try to enable it by assigning the property const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); // antialias baked into the WebGL context const renderer = new THREE.WebGLRenderer(); renderer.antialias = true; // no effect — context created without AA — edges remain aliased High https://threejs.org/docs/#api/en/renderers/WebGLRenderer
43 Performance FogExp2 for Depth and Far Culling Use scene.fog to create atmospheric depth. As a secondary benefit objects that disappear into fog before the far plane stop contributing to draw calls — useful in scenes with large view distances. Add FogExp2 to scenes with view distances above 100 units for both visual atmosphere and implicit far culling Ignore fog in scenes with far:1000+ and many distant objects that contribute tiny pixels per draw call scene.fog = new THREE.FogExp2(0x0a0a0a, 0.02); // exponential — density feels more natural than linear // far: 2000 with no fog — hundreds of distant objects too small to see still cost draw calls per frame Low https://threejs.org/docs/#api/en/scenes/FogExp2
44 Particles BufferGeometry Plus Points for Particle Systems Build all particle systems with BufferGeometry plus a Float32Array position attribute rendered as Points. Never use individual Mesh objects as particles — they cannot scale past a few hundred with good performance. Use Points plus BufferGeometry for all particle effects Create hundreds of individual Mesh objects to simulate a particle system const COUNT = 3000; const geo = new THREE.BufferGeometry(); const pos = new Float32Array(COUNT * 3); for (let i = 0; i < COUNT * 3; i++) pos[i] = (Math.random() - 0.5) * 20; geo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); const particles = new THREE.Points(geo, new THREE.PointsMaterial({ size: 0.05, color: 0xffffff })); scene.add(particles); for (let i = 0; i < 500; i++) { scene.add(new THREE.Mesh(new THREE.SphereGeometry(0.05, 8, 8), mat)); } // 500 separate draw calls per frame High https://threejs.org/docs/#api/en/objects/Points
45 Particles Particle Count Ceiling Start particle systems at 1000–3000 particles. Beyond 50000 causes sustained frame drops on mid-range mobile. Always test on a real device before increasing the count — desktop and mobile GPU performance ratios can be 10:1. Start at 3000 particles and profile on actual mobile hardware before raising the limit Set particle count at 100000 or higher without any mobile profiling const COUNT = 3000; // safe mobile baseline — profile before going higher const pos = new Float32Array(COUNT * 3); const COUNT = 150000; // 60fps on desktop — 8fps on a mid-range Android phone High https://threejs.org/docs/#api/en/objects/Points
46 Particles needsUpdate After Buffer Mutation After mutating any BufferAttribute array values per frame you must set geometry.attributes.position.needsUpdate = true so Three.js re-uploads the changed buffer to the GPU. Without it the GPU still uses the old data and particles appear completely frozen. Set needsUpdate = true on the position attribute after every per-frame mutation of the array Mutate the Float32Array values without flagging needsUpdate — positions update in JS but not on the GPU // In animate(): const pos = geo.attributes.position.array; for (let i = 0; i < pos.length; i += 3) { pos[i + 1] += Math.sin(clock.getElapsedTime() + i) * 0.001; // Y component } geo.attributes.position.needsUpdate = true; // GPU re-upload // In animate(): pos[1] += 0.001; // JS array updated — GPU buffer is stale — particles do not move Critical https://threejs.org/docs/#api/en/core/BufferAttribute.needsUpdate
47 Responsive Canvas Dimensions Not Window Size the renderer and camera to the canvas element's clientWidth and clientHeight — not window.innerWidth and innerHeight. This is correct when the canvas is inside a flex or grid container that does not fill the full viewport. Use canvas.clientWidth and canvas.clientHeight for all renderer and camera sizing Hardcode renderer size to window.innerWidth/innerHeight when the canvas may be inside a container renderer.setSize(canvas.clientWidth, canvas.clientHeight); camera.aspect = canvas.clientWidth / canvas.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); // wrong when canvas lives inside a sidebar or grid column High https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setSize
48 Responsive ResizeObserver Over window resize Event Use ResizeObserver on the canvas container instead of the window resize event. ResizeObserver fires when the container element changes size independently of the browser window — common in split-pane layouts and sidebar collapsing. Attach ResizeObserver to the canvas parent element for accurate container-aware resize detection Use only window.addEventListener('resize') for canvas sizing when the canvas is not fullscreen const ro = new ResizeObserver(entries => { const { width, height } = entries[0].contentRect; renderer.setSize(width, height); camera.aspect = width / height; camera.updateProjectionMatrix(); }); ro.observe(canvas.parentElement); window.addEventListener('resize', () => { renderer.setSize(window.innerWidth, window.innerHeight); }); // misses container-only resize events in split-pane UIs Medium https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
49 Responsive Touch Events for Mobile Interaction Add touchstart and touchmove listeners alongside mouse events so the scene remains interactive on mobile. Normalize touch coordinates to the same NDC range as mouse events and pass passive:false on touchmove if you call preventDefault. Handle both mouse and touch input for any interactive 3D scene Add only mouse event listeners and leave touch users with no interaction canvas.addEventListener('touchmove', e => { e.preventDefault(); const t = e.touches[0]; mouse.x = (t.clientX / canvas.clientWidth) * 2 - 1; mouse.y = -(t.clientY / canvas.clientHeight) * 2 + 1; }, { passive: false }); canvas.addEventListener('touchstart', e => { e.preventDefault(); }, { passive: false }); canvas.addEventListener('mousemove', handleMouse); // touch events unhandled — mobile users get no interaction Medium https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
50 Accessibility prefers-reduced-motion Check window.matchMedia('(prefers-reduced-motion: reduce)') before starting any auto-rotation, particle animation, or camera movement. Users who enable this OS preference have motion sickness or vestibular disorders. IMPORTANT: reading .matches once at page load is a one-time snapshot — if the user changes their OS accessibility setting mid-session the scene will not react. Attach a 'change' listener to the MediaQueryList so noMotion stays in sync at runtime. Use matchMedia.addEventListener('change') to keep noMotion reactive; gate all auto-animation on the live value Read .matches once at startup and never update it — the scene ignores mid-session OS setting changes const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); let noMotion = mq.matches; mq.addEventListener('change', e => { noMotion = e.matches; }); // In animate(): if (!noMotion) { mesh.rotation.y += dt * 0.8; particles.rotation.y += dt * 0.1; } const noMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; // one-time snapshot — mid-session OS change is ignored entirely High https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
51 Accessibility Canvas aria-label Add role='img' and a descriptive aria-label to renderer.domElement after appending it to the DOM. Screen readers receive no information from a WebGL canvas — the aria-label is the only description they can announce to users. Set role='img' and a meaningful aria-label on renderer.domElement before or after appending it Append the canvas to the DOM with no accessibility attributes — invisible to screen readers renderer.domElement.setAttribute('role', 'img'); renderer.domElement.setAttribute('aria-label', 'Interactive 3D product viewer. Drag to rotate. Scroll to zoom.'); document.body.appendChild(renderer.domElement); document.body.appendChild(renderer.domElement); // bare canvas — screen readers announce nothing Medium https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#accessibility_concerns
52 Production Bundler Stack for Production For production use Three.js via npm plus Vite. You get full tree-shaking reduced bundle size access to the complete examples/jsm library including OrbitControls GLTFLoader and EffectComposer and TypeScript support. Use npm install three plus Vite or Webpack for any production client-facing project Serve raw CDN script tags in a production application that needs tree-shaking or TypeScript npm install three gsap // then in your JS: import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; <!-- In a Vite/React production build: --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> // no tree-shaking — entire Three.js ships Medium https://threejs.org/docs/#manual/en/introduction/Installation
53 Production GLTFLoader with scene traverse Load 3D models using GLTFLoader and traverse gltf.scene to configure castShadow receiveShadow and material overrides on all child Mesh nodes. Calling scene.add(gltf.scene) alone silently skips all shadow and material configuration. Use GLTFLoader and traverse the entire gltf.scene graph to set up shadows and materials on every Mesh child Load a GLTF model and pass gltf.scene directly to scene.add without traversing child meshes import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; const loader = new GLTFLoader(); loader.load('model.glb', gltf => { gltf.scene.traverse(child => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); scene.add(gltf.scene); }); loader.load('model.glb', gltf => { scene.add(gltf.scene); // shadows and material setup silently skipped on all children }); Medium https://threejs.org/docs/#examples/en/loaders/GLTFLoader
54 Production LOD for Distance-Based Detail Use THREE.LOD to automatically swap high-detail and low-detail geometry as objects move closer or farther from the camera. This maintains frame rate in scenes with many objects spread across a large depth range. Use THREE.LOD to reduce triangle count on distant objects automatically Render the same high-polygon geometry for every object regardless of its distance from the camera const lod = new THREE.LOD(); lod.addLevel(highDetailMesh, 0); // used when < 15 units away lod.addLevel(medDetailMesh, 15); // 15–50 units lod.addLevel(lowDetailMesh, 50); // 50+ units scene.add(lod); scene.add(highDetailMesh); // 64k-triangle mesh rendered at full cost whether 1 unit or 100 units from camera Medium https://threejs.org/docs/#api/en/objects/LOD

View File

@@ -0,0 +1,60 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,XAML,Use WinUI XAML API surface,Uno implements the WinUI API across platforms,Microsoft.UI.Xaml namespace for all UI code,WPF or Xamarin.Forms namespaces,"using Microsoft.UI.Xaml.Controls;","using System.Windows.Controls; or using Xamarin.Forms;",High,https://platform.uno/docs/articles/implemented-views.html
2,XAML,Check API implementation status,Not all WinUI APIs are implemented on every platform,Uno API compatibility docs before using new APIs,Assuming all WinUI APIs work everywhere,"Check platform.uno/docs for API status","Using unimplemented API and discovering at runtime",High,https://platform.uno/docs/articles/implemented-views.html
3,XAML,Use Uno.WinUI not Uno.UI for new projects,Uno.WinUI uses WinUI 3 APIs,Uno.WinUI NuGet packages for new projects,Uno.UI (UWP API surface) for new projects,"<Project Sdk=""Uno.Sdk""> (Uno.WinUI / WinUI 3 surface implicit)","<PackageReference Include=""Uno.UI""/> (legacy UWP API surface)",Medium,https://platform.uno/docs/articles/updating-to-winui3.html
4,XAML,Use XAML Hot Reload,Speed up development with live XAML editing,Hot Reload for iterating on layouts,Restarting app for every XAML change,"Click Hot Reload button in VS toolbar or save in VS Code/Rider to apply XAML changes","Full rebuild for margin tweak",Medium,https://platform.uno/docs/articles/features/working-with-xaml-hot-reload.html
5,Conditional,Use platform-specific XAML,Conditional namespaces for platform-specific UI,xmlns:android xmlns:ios xmlns:wasm for platform XAML,Shared XAML when platforms need different controls,"<TextBlock android:Text=""Android"" ios:Text=""iOS"" Text=""Default""/>","#if in code-behind to set text per platform",Medium,https://platform.uno/docs/articles/platform-specific-xaml.html
6,Conditional,Use partial classes for platform code,Separate platform implementations in partial files,Partial class files with platform-specific logic,#if directives in shared code for large blocks,"MainPage.iOS.cs MainPage.Android.cs partial class files","#if __IOS__ ... #elif __ANDROID__ ... 100-line blocks in shared file",Medium,https://platform.uno/docs/articles/platform-specific-csharp.html
7,Conditional,Use preprocessor symbols correctly,Target correct platforms with defines,__IOS__ __ANDROID__ __WASM__ __DESKTOP__ for platform checks,Inventing custom symbols or checking OS at runtime,"#if __ANDROID__ Android-specific code #endif","if (RuntimeInformation.IsOSPlatform(OSPlatform.Android)) for compile-time choice",Medium,https://platform.uno/docs/articles/platform-specific-csharp.html
8,Conditional,Minimize platform-specific code,Keep shared code maximized,Abstract platform differences behind interfaces,Duplicating logic across platform files,"IDeviceService with per-platform implementation","Same 50 lines copy-pasted into iOS and Android partial classes",High,https://platform.uno/docs/articles/platform-specific-csharp.html
9,Navigation,Use Frame-based navigation,Standard WinUI navigation pattern,Frame.Navigate with page types,Manual content swapping,"rootFrame.Navigate(typeof(DetailPage), parameter);","contentPresenter.Content = new DetailPage();",Medium,https://platform.uno/docs/articles/guides/native-frame-nav-tutorial.html
10,Navigation,Use Uno.Extensions.Navigation,Type-safe navigation with DI integration,Uno.Extensions navigation for complex apps,Manual Frame management in large apps,"navigator.NavigateViewModelAsync<DetailViewModel>(this, data: item);","Frame.Navigate with string parsing everywhere",Medium,https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Navigation/NavigationOverview.html
11,Navigation,Handle platform back navigation,SystemNavigationManager.BackRequested works on Android iOS and WASM but is unimplemented on WinAppSDK desktop where calling GetForCurrentView() throws at runtime,Subscribe to BackRequested only on platforms that support it or use Uno.Toolkit NavigationBar for cross-platform back UX,Calling SystemNavigationManager.GetForCurrentView() on WinUI 3 desktop without a guard,"#if __ANDROID__ || __IOS__ || __WASM__ SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; #endif","SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; with no platform guard — crashes on Windows desktop",High,https://platform.uno/docs/articles/guides/native-frame-nav-tutorial.html
12,Navigation,Use deep linking,Support URI activation across platforms,Handle protocol activation and URI routing,Single entry point ignoring activation,Route URIs to specific pages on activation,Ignoring OnLaunched activation args,Medium,https://platform.uno/docs/articles/features/protocol-activation.html
13,Renderers,Understand Skia vs native rendering,Uno offers both rendering approaches,Skia for pixel-perfect cross-platform consistency,Assuming native rendering on all platforms,"<TargetFrameworks>net10.0-desktop;net10.0-browserwasm</TargetFrameworks> uses unified Skia Desktop shell","Expecting platform-native controls on Skia targets",High,https://platform.uno/docs/articles/features/using-skia-desktop.html
14,Renderers,Use unified net10.0-desktop target,Uno 5.2+ ships a single Skia Desktop shell that auto-selects X11 Win32 or AppKit per OS — Skia.Gtk Skia.Linux.Framebuffer and Skia.WPF heads are deprecated,net10.0-desktop TFM with UnoPlatformHostBuilder for cross-platform desktop,Targeting the legacy Skia.Gtk or Skia.Linux.Framebuffer heads in new projects,"<TargetFrameworks>net10.0-desktop</TargetFrameworks> in the Uno.Sdk single project","Per-OS Skia.Gtk Skia.MacOS Skia.Linux.Framebuffer head projects",Medium,https://platform.uno/docs/articles/features/using-skia-desktop.html
15,Renderers,Test rendering on each target,Visual differences exist between renderers,Visual testing on each active target platform,Testing only on Windows assuming others match,"Screenshot tests on iOS Android WASM and Desktop","Testing only on Windows Desktop",High,https://platform.uno/docs/articles/external/uno.uitest/doc/using-uno-uitest.html
16,Renderers,Use platform-native features when needed,Access native APIs through Uno abstractions,Native platform APIs via platform-specific code,Avoiding native features for purity,"#if __IOS__ UIKit API call #endif for camera access","Pure shared code that avoids using the camera",Medium,https://platform.uno/docs/articles/platform-specific-csharp.html
17,Performance,Optimize WASM bundle size,WebAssembly downloads can be large,IL linker and AOT for smaller WASM bundles,Default settings for production WASM,"<WasmShellILLinkerEnabled>true</WasmShellILLinkerEnabled>","Publishing WASM without linker",High,https://platform.uno/docs/articles/features/using-il-linker-webassembly.html
18,Performance,Use x:Load for deferred XAML,Defer element creation until needed,x:Load=False for hidden panels and tabs,Loading all UI elements upfront,"<StackPanel x:Load=""{x:Bind ShowAdvanced}"">","Always-loaded Collapsed panels",Medium,https://platform.uno/docs/articles/features/windows-ui-xaml-xbind.html
19,Data Binding,Use x:Bind for compiled bindings,Compiled bindings eliminate runtime reflection — Uno supports x:Bind across iOS Android WASM Skia and Windows targets that compile XAML so prefer it over {Binding} for static well-typed bindings,x:Bind for property and event bindings; reserve {Binding} for runtime-typed DataContext scenarios,{Binding} everywhere when x:Bind would compile,"<TextBlock Text=""{x:Bind ViewModel.Title, Mode=OneWay}""/>","<TextBlock Text=""{Binding Title}""/> for a statically known property",High,https://platform.uno/docs/articles/features/windows-ui-xaml-xbind.html
20,Performance,Profile per platform,Performance characteristics vary by target,Platform-specific profiling tools,Assuming desktop perf equals mobile,"Instruments on iOS and Android Profiler on Android","Profiling only on Windows",Medium,https://platform.uno/docs/articles/guides/profiling-applications.html
21,Styling,Use WinUI theme resources,Consistent theming across platforms,ThemeResource for adaptive colors,Hardcoded colors per platform,"Background=""{ThemeResource ApplicationPageBackgroundThemeBrush}""","Background=""#FFFFFF""",High,https://platform.uno/docs/articles/features/working-with-themes.html
22,Styling,Support light and dark themes,Application.RequestedTheme accepts only ApplicationTheme.Light/Dark — to follow the system theme leave it unset entirely. ElementTheme.Default exists only on FrameworkElement.RequestedTheme not on Application,Omit Application.RequestedTheme so the OS theme wins; use RequestedTheme=Default on FrameworkElement to inherit from parent,Setting Application.RequestedTheme=""Default"" — not a valid ApplicationTheme value and throws at parse time,"<Application></Application> with RequestedTheme unset; <Grid RequestedTheme=""Default"">...</Grid> on a FrameworkElement","<Application RequestedTheme=""Default""> // not a valid ApplicationTheme",Medium,https://platform.uno/docs/articles/features/working-with-themes.html
23,Styling,Use Lightweight Styling,Override control sub-properties via resources,Lightweight styling keys for minor tweaks,Full ControlTemplate for small changes,"<Button><Button.Resources><StaticResource x:Key=""ButtonBackground"" ResourceKey=""AccentBrush""/></Button.Resources></Button>","Copying entire ControlTemplate to change one color",Medium,https://platform.uno/docs/articles/external/uno.themes/doc/lightweight-styling.html
24,Styling,Test themes on each platform,Theme rendering differs across platforms,Visual theme testing on all targets,Assuming themes look identical everywhere,"Screenshot comparison across platforms for themed controls","Theming only tested on Windows",Low,https://platform.uno/docs/articles/features/working-with-themes.html
25,Architecture,Use MVVM pattern,Separate view and logic,CommunityToolkit.Mvvm or Prism for MVVM,Code-behind for business logic,"[ObservableProperty] public partial string Title { get; set; } [RelayCommand] private void Save() { }","MainPage.xaml.cs with all logic",High,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
26,Architecture,Use Uno.Extensions,Official extension libraries for common patterns,Uno.Extensions for DI navigation configuration,Building infrastructure from scratch,"Host.CreateDefaultBuilder().UseNavigation().UseConfiguration()","Manual DI and navigation setup",Medium,https://platform.uno/docs/articles/external/uno.extensions/doc/ExtensionsOverview.html
27,Architecture,Use dependency injection,Register services for testability,Microsoft.Extensions.DI through Uno.Extensions,Static service locators and singletons,"services.AddSingleton<IApiService, ApiService>();","ApiService.Instance or new ApiService() in ViewModels",Medium,https://platform.uno/docs/articles/external/uno.extensions/doc/Learn/DependencyInjection/DependencyInjectionOverview.html
28,Architecture,Share code via class libraries,Maximize code reuse across targets,Business logic in .NET Standard or shared library,Business logic in platform head projects,"MyApp.Core class library referenced by all heads","Business logic in MyApp.Wasm.csproj",Medium,https://platform.uno/docs/articles/cross-targeted-libraries.html
29,Architecture,Use Uno.Resizetizer for assets,Single source SVG to multi-platform assets,UnoImage for automatic asset generation from SVG,Manual asset export per resolution and platform,"<UnoImage Include=""Assets/icon.svg"" BaseSize=""24,24""/>","Manually exporting icon_1x.png icon_2x.png icon_3x.png per platform",Medium,https://platform.uno/docs/articles/external/uno.resizetizer/doc/using-uno-resizetizer.html
30,Accessibility,Set AutomationProperties,Enable screen readers across platforms,AutomationProperties.Name on interactive controls,Controls without accessible names,"<Button AutomationProperties.Name=""Submit form""><SymbolIcon Symbol=""Accept""/></Button>","<Button><SymbolIcon Symbol=""Accept""/></Button> without name",High,https://platform.uno/docs/articles/features/working-with-accessibility.html
31,Accessibility,Test accessibility per platform,Each platform has different assistive tech,Test with VoiceOver TalkBack and Narrator,Testing accessibility on one platform only,"VoiceOver on iOS + TalkBack on Android + Narrator on Windows","Only testing with Narrator on Windows",High,https://platform.uno/docs/articles/features/working-with-accessibility.html
32,Accessibility,Support platform text scaling,Respect user font size preferences,Dynamic font scaling for all text,Fixed font sizes ignoring accessibility,"FontSize=""{ThemeResource BodyTextBlockFontSize}""","FontSize=""14"" everywhere",Medium,https://platform.uno/docs/articles/features/working-with-accessibility.html
33,Testing,Unit test ViewModels,Test business logic independently,xUnit or MSTest on shared ViewModel code,UI testing only,"[Fact] public void LoadData_SetsItems() { vm.Load(); Assert.NotEmpty(vm.Items); }","Manual testing on each platform",Medium,https://platform.uno/docs/articles/external/uno.uitest/doc/using-uno-uitest.html
34,Testing,Use Uno.UITest for integration,Cross-platform UI testing framework,Uno.UITest for automated UI tests across platforms,Manual regression testing,"app.WaitForElement(""SaveButton""); app.Tap(""SaveButton"");","Manual click-through on each platform",Medium,https://platform.uno/docs/articles/external/uno.uitest/doc/using-uno-uitest.html
35,WASM,Show an extended splash screen on WASM,WASM bundle download and runtime startup take several seconds on first load — render branded UI immediately so users do not see a blank page (AOT and trimming are covered separately),Render a splash overlay in wwwroot/index.html that hides on first XAML navigation,Letting the user wait on a blank white page while the runtime boots,"index.html: <div id=""uno-loading"">Loading…</div> hidden via JS interop after first Frame.Navigate","No splash markup in index.html — 5-second blank page on first visit",Medium,https://platform.uno/docs/articles/external/uno.wasm.bootstrap/doc/runtime-execution-modes.html
36,WASM,Use AOT compilation for performance,Ahead-of-time compilation improves runtime speed,AOT for production WASM builds,Interpreter mode in production,"<WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode>","Default interpreter mode in production deployment",Medium,https://platform.uno/docs/articles/external/uno.wasm.bootstrap/doc/runtime-execution-modes.html
37,WASM,Handle browser limitations,WASM runs in browser sandbox,Feature detection for browser APIs,Assuming desktop capabilities in browser,"[JSImport(""globalThis.hasApi"")] static partial bool HasApi(); #if __WASM__ if (HasApi()) { ... } #endif","#if __WASM__ StorageFile.GetFileFromPathAsync(""C:/data"") #endif",Medium,https://platform.uno/docs/articles/platform-specific-csharp.html
38,Controls,Use NavigationView for app shell,WinUI NavigationView for consistent navigation across platforms,NavigationView with MenuItems for app navigation,Custom hamburger menu implementation,"<NavigationView><NavigationView.MenuItems><NavigationViewItem Content=""Home"" Icon=""Home""/></NavigationView.MenuItems></NavigationView>","Custom SplitView with manual toggle button",High,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/navigationview
39,Controls,Use ContentDialog for modal interactions,Cross-platform modal dialogs using WinUI API,ContentDialog for confirmations and input,Custom overlay Panel as dialog,"<ContentDialog Title=""Confirm"" PrimaryButtonText=""OK"" CloseButtonText=""Cancel""/>","Grid overlay with manual focus trapping",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/dialogs
40,Controls,Use CommandBar for app actions,Standard command bar with primary and secondary commands,CommandBar with AppBarButtons for toolbar actions,Custom StackPanel toolbar,"<CommandBar><AppBarButton Icon=""Save"" Label=""Save""/><AppBarButton Icon=""Delete"" Label=""Delete""/></CommandBar>","<StackPanel Orientation=""Horizontal""><Button>Save</Button></StackPanel>",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/command-bar
41,Controls,Use ToggleSwitch for boolean settings,Platform-native toggle control for on/off preferences,ToggleSwitch for settings and feature flags,CheckBox for toggle settings,"<ToggleSwitch Header=""Dark Mode"" IsOn=""{x:Bind ViewModel.IsDarkMode, Mode=TwoWay}""/>","<CheckBox Content=""Enable dark mode""/>",Low,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/toggles
42,Data Binding,Implement INotifyPropertyChanged,Enable UI updates when ViewModel properties change,CommunityToolkit.Mvvm [ObservableProperty] for auto-notification,Properties without change notification,"[ObservableProperty] public partial string Title { get; set; }","public string Title { get; set; } without notification",High,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
43,Data Binding,Use ObservableCollection for bound lists,Collection change notifications for ItemsSources across platforms,ObservableCollection<T> for data-bound lists,List<T> for bound ItemsSources,"ObservableCollection<Item> Items { get; } = new();","List<Item> Items { get; set; } = new();",High,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
44,Lifecycle,Handle app suspension on mobile,iOS and Android may suspend or terminate the app — WinAppSDK desktop does not raise Suspending so use window Closed for desktop save-state,Save state in OnSuspending and restore on activation,Ignoring lifecycle losing user state on mobile,"Application.Current.Suspending += (s, e) => { var d = e.SuspendingOperation.GetDeferral(); SaveState(); d.Complete(); };","No suspend handler losing form data on mobile",High,https://platform.uno/docs/articles/features/windows-ui-xaml-application.html
45,Lifecycle,Use Uno.Extensions.Hosting for startup,Structured app initialization with DI and configuration,IHost builder pattern for app startup and service registration,Manual initialization in App constructor,"Host.CreateDefaultBuilder().ConfigureServices(s => s.AddSingleton<MainViewModel>()).Build();","new MainViewModel() in App.xaml.cs constructor",Medium,https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Hosting/HostingOverview.html
46,Performance,Use ListView virtualization for large lists,Only renders visible items to reduce memory and layout cost,ListView with default ItemsStackPanel virtualization,ItemsControl or StackPanel for large data sets,"<ListView ItemsSource=""{x:Bind Items}""/> (virtualizes by default)","<ItemsControl><StackPanel> rendering 5000 items at once",High,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/listview-and-gridview
47,Accessibility,Support keyboard navigation on desktop,Skia and WinAppSDK targets need full keyboard operability — note TabIndex routing is not fully implemented on every Uno target,AccessKey and KeyboardAccelerator on Skia and WinAppSDK targets,Mouse-only interactions on desktop,"<Button AccessKey=""S"" Content=""Save""><Button.KeyboardAccelerators><KeyboardAccelerator Modifiers=""Control"" Key=""S""/></Button.KeyboardAccelerators></Button>","Clickable controls without keyboard support on desktop",High,https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-accelerators
48,WASM,Use service workers for offline support,Enable PWA capabilities for WASM deployments,Service worker registration for caching and offline mode,Online-only WASM app with no offline fallback,"<WasmPWAManifestFile>manifest.webmanifest</WasmPWAManifestFile>","No service worker leaving WASM app unusable offline",Low,https://platform.uno/docs/articles/external/uno.wasm.bootstrap/doc/features-pwa.html
49,Performance,Marshal to UI thread with DispatcherQueue,Cross-thread access to UI elements throws — capture the UI DispatcherQueue once and use TryEnqueue to update from background work,DispatcherQueue.GetForCurrentThread().TryEnqueue from background work,Touching UI controls directly from a Task,"_dispatcher.TryEnqueue(() => StatusText.Text = ""Done"");","await Task.Run(() => StatusText.Text = ""Done""); throws on non-UI thread",High,https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue
50,Styling,Merge XamlControlsResources in App.xaml,Required for Fluent control styles to load — without it controls render with no template,Add XamlControlsResources at the top of Application.Resources MergedDictionaries,Skipping the merged dictionary and wondering why Buttons look unstyled,"<Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><XamlControlsResources xmlns=""using:Microsoft.UI.Xaml.Controls""/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources>","<Application.Resources><SolidColorBrush x:Key=""MyBrush"" Color=""Red""/></Application.Resources> with no XamlControlsResources merged",High,https://platform.uno/docs/articles/features/fluent-styles.html
51,Architecture,Use async [RelayCommand] for I/O,AsyncRelayCommand reports CanExecute=false (raising CanExecuteChanged) and exposes IsRunning while the Task is in flight — the bound control is disabled and re-entrancy is prevented by default (AllowConcurrentExecutions=false),[RelayCommand] on a Task-returning method for awaitable work,async void event handlers calling .Wait() or .Result,"[RelayCommand] private async Task LoadAsync() { Items = await _api.GetAsync(); }","public void OnLoadClick(object s, EventArgs e) { LoadAsync().Wait(); } deadlock risk",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand
52,Architecture,Use x:Uid for localized strings,WinUI x:Uid resolves UI text from .resw resources at runtime — use it instead of hardcoded strings to support localization across iOS Android WASM and desktop from a single project,x:Uid on every user-facing string with matching .resw entries per language under Strings/{lang}/Resources.resw,Hardcoding language-specific strings into XAML or code-behind,"<Button x:Uid=""SubmitButton""/> with Strings/en/Resources.resw entry SubmitButton.Content=Submit","<Button Content=""Submit""/> hardcoded in XAML",High,https://platform.uno/docs/articles/features/working-with-strings.html
53,Architecture,Wire up ILogger via Uno.Extensions.Logging,Cross-platform logging routes to platform-native sinks (OSLog on iOS Console on WASM Debug elsewhere) when configured through the IHost builder,Inject ILogger<T> into ViewModels and services and call UseLogging() on the host builder,Console.WriteLine or platform-specific log APIs scattered across shared code,"Host.CreateDefaultBuilder().UseLogging(c => c.SetMinimumLevel(LogLevel.Information)) and ILogger<MainViewModel> via constructor injection","Console.WriteLine(""error"") in shared code with no platform-aware routing",Medium,https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Logging/LoggingOverview.html
54,Performance,Enable PublishAot on net10.0-desktop,Skia Desktop on .NET 10 supports Native AOT for faster cold start and smaller deployments — opt in per-target so debug builds remain fast,<PublishAot>true</PublishAot> in a TFM-conditional PropertyGroup for net10.0-desktop release builds,Enabling PublishAot globally and breaking debug iteration on every TFM,"<PropertyGroup Condition=""'$(TargetFramework)'=='net10.0-desktop' AND '$(Configuration)'=='Release'""><PublishAot>true</PublishAot></PropertyGroup>","<PublishAot>true</PublishAot> at root with no TFM/Configuration condition",Medium,https://platform.uno/docs/articles/features/using-skia-desktop.html
55,Performance,Never block on async with .Result or .Wait(),Blocking on a Task from the UI thread deadlocks because the awaiter cannot resume on the captured SynchronizationContext — always await async APIs through to the event handler,Await async methods all the way up; in libraries call ConfigureAwait(false) to avoid context capture,Calling .Result .Wait() or GetAwaiter().GetResult() on a Task from the UI thread,"private async void OnLoadClick(object s, RoutedEventArgs e) { var data = await _api.GetAsync(); Items = data; }","private void OnLoadClick(object s, RoutedEventArgs e) { var data = _api.GetAsync().Result; } // deadlocks on UI thread",High,https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
56,Styling,Define ThemeDictionaries for Light Dark and HighContrast,Resources placed inside ResourceDictionary.ThemeDictionaries entries are automatically swapped when the system theme changes — required for theme-aware brushes,Wrap brushes in a ThemeDictionaries dictionary keyed by Light Dark and HighContrast in App.xaml or page resources,Defining a single brush at the root and missing dark/high-contrast variants,"<ResourceDictionary.ThemeDictionaries><ResourceDictionary x:Key=""Light""><SolidColorBrush x:Key=""Brand"" Color=""#005A9E""/></ResourceDictionary><ResourceDictionary x:Key=""Dark""><SolidColorBrush x:Key=""Brand"" Color=""#3A96DD""/></ResourceDictionary></ResourceDictionary.ThemeDictionaries>","<SolidColorBrush x:Key=""Brand"" Color=""#005A9E""/> at root with no theme variants",Medium,https://platform.uno/docs/articles/features/working-with-themes.html
57,Architecture,Use Uno.Sdk with UnoFeatures,Uno.Sdk is the modern single-project SDK that auto-resolves Uno.WinUI Uno.Toolkit Material and other packages from a UnoFeatures property — declare features by name instead of hand-managing dozens of PackageReferences,Declare features in the csproj via <UnoFeatures>...</UnoFeatures> and let the SDK resolve transitive packages,Hand-adding every Uno.* PackageReference and matching version numbers across packages,"<Project Sdk=""Uno.Sdk""><PropertyGroup><UnoFeatures>Material;Hosting;Toolkit;Logging;MVVM</UnoFeatures></PropertyGroup></Project>","<PackageReference Include=""Uno.WinUI""/><PackageReference Include=""Uno.Material.WinUI""/> ... duplicated per feature with mismatched versions",Medium,https://platform.uno/docs/articles/features/using-the-uno-sdk.html
58,Lifecycle,Persist desktop window state via Window.Closed,WinAppSDK and Skia desktop heads do not raise Application.Suspending — handle the Window.Closed event (and AppWindow size/position changes) to save user state when desktop apps shut down,Subscribe to MainWindow.Closed and persist any unsaved state before the window is destroyed,Relying on Application.Suspending to fire on desktop targets,"m_window.Closed += (s, e) => SaveState();","Application.Current.Suspending += SaveState; // never fires on WinAppSDK or Skia desktop",Medium,https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.window
59,Architecture,Use WinRT.Interop for native window handle on Windows,Calling Win32 APIs from a WinUI Window (file pickers icon embedding etc.) requires the HWND — retrieve it via WinRT.Interop.WindowNative.GetWindowHandle and guard the call so non-Windows targets stay unaffected,GetWindowHandle inside a #if WINDOWS block when you need the HWND,Calling WinRT.Interop in shared code without a platform guard,"#if WINDOWS\nvar hWnd = WinRT.Interop.WindowNative.GetWindowHandle(MainWindow);\n#endif","var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(MainWindow); // breaks build on iOS Android WASM",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/retrieve-hwnd
Can't render this file because it contains an unexpected character in line 23 and column 412.

View File

@@ -0,0 +1,56 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,XAML,Use x:Bind for compiled bindings,Compile-time validated bindings with better performance,x:Bind for type-safe performant bindings,{Binding} when x:Bind is available,"<TextBlock Text=""{x:Bind ViewModel.Name, Mode=OneWay}""/>","<TextBlock Text=""{Binding Name}""/>",High,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
2,XAML,Use x:Load for deferred elements,Delay creation of elements until needed,x:Load=False for hidden or conditional panels,Loading all UI elements at page load,"<StackPanel x:Name=""AdvancedPanel"" x:Load=""{x:Bind ShowAdvanced, Mode=OneWay}"">","Collapsed StackPanel that is always instantiated",Medium,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-load-attribute
3,XAML,Use x:Phase for incremental item rendering,Render list items in priority phases,x:Phase on secondary content in item DataTemplates,All template content loaded in one pass,"<TextBlock x:Phase=""1"" Text=""{x:Bind Description}""/>","Complex DataTemplate with no phasing",Medium,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-phase-attribute
4,XAML,Use x:DefaultBindMode,Reduce repetitive Mode= declarations,x:DefaultBindMode=OneWay on containers,Mode=OneWay on every individual x:Bind,"<StackPanel x:DefaultBindMode=""OneWay"">","Mode=OneWay repeated on every binding in a panel",Low,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
5,XAML,Use x:DeferLoadStrategy for legacy support,Deferred loading before x:Load was available,x:Load (preferred) or x:DeferLoadStrategy=Lazy,Eagerly loading rarely shown UI,"x:Load=""False"" (or x:DeferLoadStrategy=""Lazy"" on older targets)","Always-loaded panels toggled with Visibility",Low,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-deferloadstrategy-attribute
6,Controls,Use NavigationView for app shell,Standard UWP navigation pattern with hamburger menu,NavigationView for top-level navigation,Custom SplitView hamburger menu,"<NavigationView><NavigationView.MenuItems><NavigationViewItem Content=""Home"" Icon=""Home""/></NavigationView.MenuItems></NavigationView>","Custom SplitView with manual toggle button",High,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/navigationview
7,Controls,Use CommandBar for app actions,Standard app bar for primary commands,CommandBar with AppBarButtons,Custom StackPanel toolbar,"<CommandBar><AppBarButton Icon=""Save"" Label=""Save""/></CommandBar>","<StackPanel Orientation=""Horizontal""><Button>Save</Button></StackPanel>",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/command-bar
8,Controls,Use ContentDialog for modals,System-styled modal dialogs,ContentDialog for confirmations and input,Custom popup overlays,"<ContentDialog Title=""Confirm"" PrimaryButtonText=""Yes"" CloseButtonText=""No""/>","Grid overlay with manual focus trapping",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/dialogs-and-flyouts/dialogs
9,Controls,Use AutoSuggestBox for search,Built-in search box with suggestions,AutoSuggestBox with QuerySubmitted and SuggestionChosen,TextBox with manual suggestion popup,"<AutoSuggestBox QueryIcon=""Find"" TextChanged=""OnTextChanged"" SuggestionChosen=""OnChosen"" QuerySubmitted=""OnQuerySubmitted""/>","TextBox with custom Popup and ListBox for suggestions",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/auto-suggest-box
10,Controls,Use CalendarDatePicker and TimePicker,Platform-consistent date and time selection,Built-in date and time pickers,Custom date selection controls,"<CalendarDatePicker Header=""Start date""/><TimePicker Header=""Time""/>","TextBox with date parsing and validation",Low,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/date-and-time
11,Controls,Use PersonPicture for user avatars,Consistent avatar display with fallback initials,PersonPicture with DisplayName and ProfilePicture,Custom Ellipse with ImageBrush for avatars,"<PersonPicture DisplayName=""Jane Doe"" ProfilePicture=""{x:Bind AvatarUri}""/>","<Ellipse><Ellipse.Fill><ImageBrush ImageSource=""avatar.png""/></Ellipse.Fill></Ellipse>",Low,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/person-picture
12,Styling,Use ThemeResource for adaptive colors,Colors that switch with light and dark theme,ThemeResource for all color references,Hardcoded hex values that break in dark mode,"Foreground=""{ThemeResource SystemControlForegroundBaseHighBrush}""","Foreground=""#000000""",High,https://learn.microsoft.com/en-us/windows/uwp/design/style/color
13,Styling,Use Fluent Design materials,Acrylic translucent material for depth,Built-in Fluent materials for depth and motion,Custom shader effects for blur and reveal,"<Grid Background=""{ThemeResource SystemControlAcrylicWindowBrush}""/>","Custom CompositionEffectBrush recreating acrylic",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/style/acrylic
14,Styling,Use Lightweight Styling,Override control resource keys for subtle changes,Lightweight styling resource overrides,Full ControlTemplate copy for small tweaks,"<Button><Button.Resources><SolidColorBrush x:Key=""ButtonBackground"" Color=""Blue""/></Button.Resources></Button>","Entire ControlTemplate copied to change background",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles#lightweight-styling
15,Styling,Use implicit styles for consistency,TargetType without x:Key applies to all instances,Implicit Style for default control appearance,Repeating Setters on every control instance,"<Style TargetType=""Button""><Setter Property=""CornerRadius"" Value=""4""/></Style>","CornerRadius=""4"" on every Button in the page",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles
16,Styling,Use VisualStateManager for visual states,Define visual states with Setters that change properties when triggered,VisualStateGroup containing VisualStates with Setter targets,Toggling Visibility from code-behind on SizeChanged,"<VisualStateGroup><VisualState x:Name=""Wide""><VisualState.Setters><Setter Target=""root.Orientation"" Value=""Horizontal""/></VisualState.Setters></VisualState></VisualStateGroup>","SizeChanged handler that flips Orientation in code-behind",High,https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
17,Navigation,Use Frame for page navigation,Windows.UI.Xaml.Controls.Frame for UWP page navigation,Frame.Navigate with typed parameters,Swapping UserControls manually,"rootFrame.Navigate(typeof(DetailPage), itemId);","contentArea.Content = new DetailPage();",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/basics/navigate-between-two-pages
18,Navigation,Handle back button correctly,Provide an in-app Back button styled with NavigationBackButtonNormalStyle and handle SystemNavigationManager.BackRequested for hardware back gamepad B and Tablet-Mode back; also handle CoreDispatcher.AcceleratorKeyActivated for Alt+Left,In-app NavigationBackButtonNormalStyle button plus SystemNavigationManager.BackRequested handler,Relying on the deprecated title-bar back button (AppViewBackButtonVisibility) or ignoring system back signals,"SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;","No back button support on phone or tablet",High,https://learn.microsoft.com/en-us/windows/uwp/ui-input/back-navigation
19,Navigation,Support deep linking with protocol activation,Respond to URI activation and toast taps,OnActivated handler with proper page routing,Ignoring activation arguments,"protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { ... } }","Empty OnActivated ignoring URI parameters",Medium,https://learn.microsoft.com/en-us/windows/uwp/launch-resume/handle-uri-activation
20,Navigation,Use ConnectedAnimations for continuity,Smooth transitions between pages,ConnectedAnimationService for shared element transitions,Abrupt page transitions with no visual continuity,"ConnectedAnimationService.GetForCurrentView().PrepareToAnimate(""image"", sourceImage);","No transition animation between list and detail",Low,https://learn.microsoft.com/en-us/windows/uwp/design/motion/connected-animation
21,Data Binding,Implement INotifyPropertyChanged,Enable UI updates on property changes,INotifyPropertyChanged on all ViewModels,Auto-properties without notification,"public string Title { get => _title; set { _title = value; OnPropertyChanged(); } }","public string Title { get; set; } expecting UI updates",High,https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
22,Data Binding,Use ObservableCollection for lists,Collection change notifications for ItemsSources,ObservableCollection<T> for bound lists,List<T> for data-bound collections,"ObservableCollection<Item> Items { get; } = new();","List<Item> Items { get; set; } = new();",High,https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
23,Data Binding,Use function bindings with x:Bind,Call static methods directly in markup,x:Bind to static converter methods,IValueConverter for trivial transforms,"<TextBlock Visibility=""{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}""/>","Full IValueConverter class for bool to Visibility",Medium,https://learn.microsoft.com/en-us/windows/uwp/data-binding/function-bindings
24,Data Binding,Specify Mode on x:Bind,x:Bind defaults to OneTime not OneWay,Mode=OneWay or TwoWay when live updates needed,Omitting Mode and getting stale UI,"Text=""{x:Bind Title, Mode=OneWay}""","Text=""{x:Bind Title}"" expecting live updates",High,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
25,Data Binding,Use CollectionViewSource for grouping,Group and sort collections declaratively,CollectionViewSource for grouped ListView and GridView,Manual grouping logic in code-behind,"<CollectionViewSource x:Key=""GroupedItems"" IsSourceGrouped=""True"" Source=""{x:Bind GroupedData}""/>","Manual loop building grouped StackPanels",Medium,https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
26,Performance,Use ListView and GridView virtualization,Only creates containers for visible items,Default virtualization in ListView and GridView,Setting ItemsPanel to non-virtualizing panel,"<ListView ItemsSource=""{x:Bind Items}""/> (virtualizes by default)","<ListView><ListView.ItemsPanel><ItemsPanelTemplate><StackPanel/></ItemsPanelTemplate></ListView.ItemsPanel></ListView>",High,https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview
27,Performance,Use ISupportIncrementalLoading,Load data on demand as user scrolls,ISupportIncrementalLoading for large datasets,Loading entire collection upfront,"class IncrementalSource : ObservableCollection<Item>, ISupportIncrementalLoading","await LoadAll() loading 50K items at startup",Medium,https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
28,Performance,Reduce XAML visual tree depth,Simpler trees layout and render faster,Flat templates with minimal nesting,Deeply nested panels in DataTemplates,"<StackPanel><TextBlock/><TextBlock/></StackPanel> in item template","<Grid><Border><StackPanel><Grid>... 8 levels in item template",Medium,https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-xaml-loading
29,Performance,Use compiled bindings in DataTemplates,x:Bind in templates requires x:DataType,x:DataType on DataTemplate for compiled bindings,{Binding} in item templates for large lists,"<DataTemplate x:DataType=""local:Item""><TextBlock Text=""{x:Bind Name}""/></DataTemplate>","<DataTemplate><TextBlock Text=""{Binding Name}""/></DataTemplate>",High,https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
30,Performance,Profile with Visual Studio diagnostics,Measure before optimizing,Application Timeline and Memory Usage tools,Guessing at performance problems,"VS Diagnostic Tools > Application Timeline","Optimizing without profiling data",Medium,https://learn.microsoft.com/en-us/visualstudio/profiling/application-timeline
31,Threading,Use async/await for all IO,Keep UI thread responsive,async/await for file network and database operations,Synchronous IO blocking the UI thread,"var file = await StorageFile.GetFileFromPathAsync(path);","StorageFile.GetFileFromPathAsync(path).AsTask().Result;",High,https://learn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
32,Threading,Use CoreDispatcher for UI thread access,Post work back to the UI thread from background,Dispatcher.RunAsync from background threads,Touching UI elements from background threads,"await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Status = ""Done"");","textBlock.Text = ""Done"" from Task.Run",High,https://learn.microsoft.com/en-us/uwp/api/windows.ui.core.coredispatcher
33,Threading,Offload CPU work with Task.Run,Keep compute-heavy work off UI thread,Task.Run for CPU-bound operations,Heavy computation blocking UI,"var result = await Task.Run(() => ProcessData(items));","var result = ProcessData(items); freezing UI",High,https://learn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
34,Threading,Use IProgress for status updates,Report progress from background operations,IProgress<T> for progress reporting to UI,Polling shared variables for progress,"var progress = new Progress<int>(p => ProgressBar.Value = p); await Task.Run(() => Process(progress));","while (!done) { await Task.Delay(100); check shared field; }",Medium,https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
35,Adaptive,Use AdaptiveTrigger for responsive layouts,MinWindowWidth and MinWindowHeight triggers fire at standard breakpoints (640 small / 1008 medium),AdaptiveTrigger inside VisualState.StateTriggers with the 640 and 1008 breakpoints,Fixed layouts for a single screen size,"<VisualState.StateTriggers><AdaptiveTrigger MinWindowWidth=""640""/></VisualState.StateTriggers>","Single-column layout at all widths",High,https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
36,Adaptive,Design for multiple device families,Phone tablet desktop Xbox and HoloLens,DeviceFamily-specific views and resources,Desktop-only design ignoring other form factors,"DeviceFamily-Mobile/MainPage.xaml for phone-specific layout","Fixed 1920x1080 layout",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/layout/screen-sizes-and-breakpoints-for-responsive-design
37,Adaptive,Use RelativePanel for adaptive positioning,Controls position relative to each other,RelativePanel for layouts that reflow at breakpoints,Absolute positioning or fixed margins,"<Button RelativePanel.Below=""title"" RelativePanel.AlignLeftWithPanel=""True""/>","<Button Margin=""0,60,0,0""/> calculated from title height",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml#relativepanel
38,Adaptive,Support multi-window with secondary views,Open detached views with CoreApplication.CreateNewView and ApplicationViewSwitcher,CreateNewView and TryShowAsStandaloneAsync for multi-document scenarios,Single-window assumptions when scenarios benefit from secondary views,"var view = CoreApplication.CreateNewView(); await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { /* set content */ });","Modal overlay used for content that should be a separate window",Medium,https://learn.microsoft.com/en-us/windows/uwp/design/layout/show-multiple-views
39,Accessibility,Set AutomationProperties,Enable Narrator and screen reader support,AutomationProperties.Name on all interactive controls,Controls without accessible names,"<AppBarButton AutomationProperties.Name=""Save document"" Icon=""Save""/>","<AppBarButton Icon=""Save""/> without name",High,https://learn.microsoft.com/en-us/windows/uwp/design/accessibility/basic-accessibility-information
40,Accessibility,Support keyboard and gamepad,All functions reachable without touch,Tab navigation XYFocus and access keys,Touch-only interactions,"<Button AccessKey=""S"" XYFocusDown=""{x:Bind OtherButton}""/>","No keyboard or gamepad support",High,https://learn.microsoft.com/en-us/windows/uwp/design/input/keyboard-interactions
41,Accessibility,Support contrast themes,Respect system contrast themes (renamed from high contrast in Windows 11),ThemeResource brushes that adapt to contrast themes,Hardcoded colors that vanish under contrast themes,"Foreground=""{ThemeResource SystemControlForegroundBaseHighBrush}""","Foreground=""#444444""",High,https://learn.microsoft.com/en-us/windows/uwp/design/accessibility/high-contrast-themes
42,Accessibility,Test with Narrator and Accessibility Insights,Validate screen reader and automation compliance,Regular Narrator walkthrough and Accessibility Insights scan,Shipping without accessibility testing,"Accessibility Insights FastPass on every page","No accessibility testing before release",Medium,https://accessibilityinsights.io/
43,Architecture,Use MVVM pattern,Separate View ViewModel and Model,ViewModel with INotifyPropertyChanged and ICommand,Business logic in code-behind,"ViewModel bound via DataContext with commands","MainPage.xaml.cs with database calls and UI logic",Medium,https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-and-mvvm
44,Architecture,Use Template Studio for scaffolding,Proven project templates with navigation and services,Windows Template Studio for new UWP projects,Blank project with manual boilerplate,"Template Studio with MVVM Toolkit and navigation service","Blank App template building everything from scratch",Low,https://github.com/microsoft/TemplateStudio
45,Architecture,Use dependency injection,Register services for testability,Microsoft.Extensions.DI for service resolution,Static singletons and manual construction,"services.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>();","DataService.Instance or new DataService() everywhere",Medium,https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
46,Architecture,Keep platform APIs behind abstractions,Isolate WinRT APIs from business logic,Interfaces wrapping StorageFile FilePicker etc,Direct WinRT calls in ViewModels,"IFileService wrapping FileOpenPicker and StorageFile","FileOpenPicker usage directly in ViewModel",Medium,https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-and-mvvm
47,Lifecycle,Handle suspend and resume,UWP apps are suspended when not in foreground,Save state in OnSuspending and restore in OnLaunched,Ignoring app lifecycle losing user state,"Application.Current.Suspending += (s, e) => SaveState();","No suspend handler losing in-progress form data",High,https://learn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle
48,Lifecycle,Use ExtendedExecutionSession for background work,Request extended time for unfinished operations,ExtendedExecutionSession for saving or uploads,Assuming background work completes after suspend,"var session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.SavingData };","Long upload with no extended execution that gets killed on suspend",Medium,https://learn.microsoft.com/en-us/windows/uwp/launch-resume/run-minimized-with-extended-execution
49,Lifecycle,Handle prelaunch,Apps must opt in to prelaunch via CoreApplication.EnablePrelaunch(true) starting in Windows 10 1607; check LaunchActivatedEventArgs.PrelaunchActivated to skip user-visible work,Opt in with EnablePrelaunch and skip heavy init when PrelaunchActivated is true,Performing full initialization or navigating during prelaunch,"CoreApplication.EnablePrelaunch(true); if (e.PrelaunchActivated) return; // skip heavy init","Loading all data and navigating on prelaunch",Medium,https://learn.microsoft.com/en-us/windows/uwp/launch-resume/handle-app-prelaunch
50,Testing,Unit test ViewModels,Test logic without UI framework dependencies,xUnit or MSTest on ViewModel methods,Testing only through the running app,"[Fact] public async Task Load_PopulatesItems() { await vm.LoadAsync(); Assert.NotEmpty(vm.Items); }","Manual testing by tapping through the app",Medium,https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
51,Testing,Use WinAppDriver with Appium for UI tests,Automated UI testing for UWP (Coded UI Test was deprecated in Visual Studio 2019); WinAppDriver v1 is in low-maintenance mode and Appium 2 is the modern direction,WinAppDriver with Appium for end-to-end tests,Manual regression testing,"session.FindElementByAccessibilityId(""SaveButton"").Click();","Manual click-through testing before each release",Medium,https://github.com/microsoft/WinAppDriver
52,Testing,Test on multiple device families,Behavior varies across phone desktop and Xbox,Test on device emulators and real hardware,Desktop-only testing,"Test on Mobile emulator and Xbox dev mode","Only running on local desktop",Medium,https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/device-portal
53,Architecture,Prefer WinUI 3 for new projects,UWP is in maintenance mode and Microsoft recommends WinUI 3 and Windows App SDK for new development,WinUI 3 with Windows App SDK for new desktop apps,Starting new projects on UWP when WinUI 3 is available,"New project with Microsoft.WindowsAppSDK and WinUI 3","New UWP project for a desktop-only app in 2024+",Medium,https://learn.microsoft.com/en-us/windows/apps/get-started/
54,Architecture,Plan migration to Windows App SDK,Microsoft provides migration guides from UWP to WinUI 3,Incremental migration using XAML Islands or full port to WinUI 3,Ignoring migration path and accumulating UWP technical debt,"Follow UWP to WinUI 3 migration guide for existing apps","Continuing major feature development on UWP without migration plan",Medium,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/migrate-to-windows-app-sdk/overall-migration-strategy
55,Lifecycle,Use a deferral when saving async state on suspend,Suspending grants only ~5 seconds before the OS may terminate; await work needs SuspendingOperation.GetDeferral and Complete or save returns before it finishes,GetDeferral around async save calls and Complete in finally,Async work that returns the suspending handler before completion,"async void OnSuspending(object s, SuspendingEventArgs e) { var d = e.SuspendingOperation.GetDeferral(); try { await SaveAsync(); } finally { d.Complete(); } }","async void OnSuspending(object s, SuspendingEventArgs e) { await SaveAsync(); } // handler returns before save completes",Medium,https://learn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 XAML Use x:Bind for compiled bindings Compile-time validated bindings with better performance x:Bind for type-safe performant bindings {Binding} when x:Bind is available <TextBlock Text="{x:Bind ViewModel.Name, Mode=OneWay}"/> <TextBlock Text="{Binding Name}"/> High https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
3 2 XAML Use x:Load for deferred elements Delay creation of elements until needed x:Load=False for hidden or conditional panels Loading all UI elements at page load <StackPanel x:Name="AdvancedPanel" x:Load="{x:Bind ShowAdvanced, Mode=OneWay}"> Collapsed StackPanel that is always instantiated Medium https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-load-attribute
4 3 XAML Use x:Phase for incremental item rendering Render list items in priority phases x:Phase on secondary content in item DataTemplates All template content loaded in one pass <TextBlock x:Phase="1" Text="{x:Bind Description}"/> Complex DataTemplate with no phasing Medium https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-phase-attribute
5 4 XAML Use x:DefaultBindMode Reduce repetitive Mode= declarations x:DefaultBindMode=OneWay on containers Mode=OneWay on every individual x:Bind <StackPanel x:DefaultBindMode="OneWay"> Mode=OneWay repeated on every binding in a panel Low https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
6 5 XAML Use x:DeferLoadStrategy for legacy support Deferred loading before x:Load was available x:Load (preferred) or x:DeferLoadStrategy=Lazy Eagerly loading rarely shown UI x:Load="False" (or x:DeferLoadStrategy="Lazy" on older targets) Always-loaded panels toggled with Visibility Low https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-deferloadstrategy-attribute
7 6 Controls Use NavigationView for app shell Standard UWP navigation pattern with hamburger menu NavigationView for top-level navigation Custom SplitView hamburger menu <NavigationView><NavigationView.MenuItems><NavigationViewItem Content="Home" Icon="Home"/></NavigationView.MenuItems></NavigationView> Custom SplitView with manual toggle button High https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/navigationview
8 7 Controls Use CommandBar for app actions Standard app bar for primary commands CommandBar with AppBarButtons Custom StackPanel toolbar <CommandBar><AppBarButton Icon="Save" Label="Save"/></CommandBar> <StackPanel Orientation="Horizontal"><Button>Save</Button></StackPanel> Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/command-bar
9 8 Controls Use ContentDialog for modals System-styled modal dialogs ContentDialog for confirmations and input Custom popup overlays <ContentDialog Title="Confirm" PrimaryButtonText="Yes" CloseButtonText="No"/> Grid overlay with manual focus trapping Medium https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/dialogs-and-flyouts/dialogs
10 9 Controls Use AutoSuggestBox for search Built-in search box with suggestions AutoSuggestBox with QuerySubmitted and SuggestionChosen TextBox with manual suggestion popup <AutoSuggestBox QueryIcon="Find" TextChanged="OnTextChanged" SuggestionChosen="OnChosen" QuerySubmitted="OnQuerySubmitted"/> TextBox with custom Popup and ListBox for suggestions Medium https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/auto-suggest-box
11 10 Controls Use CalendarDatePicker and TimePicker Platform-consistent date and time selection Built-in date and time pickers Custom date selection controls <CalendarDatePicker Header="Start date"/><TimePicker Header="Time"/> TextBox with date parsing and validation Low https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/date-and-time
12 11 Controls Use PersonPicture for user avatars Consistent avatar display with fallback initials PersonPicture with DisplayName and ProfilePicture Custom Ellipse with ImageBrush for avatars <PersonPicture DisplayName="Jane Doe" ProfilePicture="{x:Bind AvatarUri}"/> <Ellipse><Ellipse.Fill><ImageBrush ImageSource="avatar.png"/></Ellipse.Fill></Ellipse> Low https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/person-picture
13 12 Styling Use ThemeResource for adaptive colors Colors that switch with light and dark theme ThemeResource for all color references Hardcoded hex values that break in dark mode Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}" Foreground="#000000" High https://learn.microsoft.com/en-us/windows/uwp/design/style/color
14 13 Styling Use Fluent Design materials Acrylic translucent material for depth Built-in Fluent materials for depth and motion Custom shader effects for blur and reveal <Grid Background="{ThemeResource SystemControlAcrylicWindowBrush}"/> Custom CompositionEffectBrush recreating acrylic Medium https://learn.microsoft.com/en-us/windows/uwp/design/style/acrylic
15 14 Styling Use Lightweight Styling Override control resource keys for subtle changes Lightweight styling resource overrides Full ControlTemplate copy for small tweaks <Button><Button.Resources><SolidColorBrush x:Key="ButtonBackground" Color="Blue"/></Button.Resources></Button> Entire ControlTemplate copied to change background Medium https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles#lightweight-styling
16 15 Styling Use implicit styles for consistency TargetType without x:Key applies to all instances Implicit Style for default control appearance Repeating Setters on every control instance <Style TargetType="Button"><Setter Property="CornerRadius" Value="4"/></Style> CornerRadius="4" on every Button in the page Medium https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles
17 16 Styling Use VisualStateManager for visual states Define visual states with Setters that change properties when triggered VisualStateGroup containing VisualStates with Setter targets Toggling Visibility from code-behind on SizeChanged <VisualStateGroup><VisualState x:Name="Wide"><VisualState.Setters><Setter Target="root.Orientation" Value="Horizontal"/></VisualState.Setters></VisualState></VisualStateGroup> SizeChanged handler that flips Orientation in code-behind High https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
18 17 Navigation Use Frame for page navigation Windows.UI.Xaml.Controls.Frame for UWP page navigation Frame.Navigate with typed parameters Swapping UserControls manually rootFrame.Navigate(typeof(DetailPage), itemId); contentArea.Content = new DetailPage(); Medium https://learn.microsoft.com/en-us/windows/uwp/design/basics/navigate-between-two-pages
19 18 Navigation Handle back button correctly Provide an in-app Back button styled with NavigationBackButtonNormalStyle and handle SystemNavigationManager.BackRequested for hardware back gamepad B and Tablet-Mode back; also handle CoreDispatcher.AcceleratorKeyActivated for Alt+Left In-app NavigationBackButtonNormalStyle button plus SystemNavigationManager.BackRequested handler Relying on the deprecated title-bar back button (AppViewBackButtonVisibility) or ignoring system back signals SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; No back button support on phone or tablet High https://learn.microsoft.com/en-us/windows/uwp/ui-input/back-navigation
20 19 Navigation Support deep linking with protocol activation Respond to URI activation and toast taps OnActivated handler with proper page routing Ignoring activation arguments protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { ... } } Empty OnActivated ignoring URI parameters Medium https://learn.microsoft.com/en-us/windows/uwp/launch-resume/handle-uri-activation
21 20 Navigation Use ConnectedAnimations for continuity Smooth transitions between pages ConnectedAnimationService for shared element transitions Abrupt page transitions with no visual continuity ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("image", sourceImage); No transition animation between list and detail Low https://learn.microsoft.com/en-us/windows/uwp/design/motion/connected-animation
22 21 Data Binding Implement INotifyPropertyChanged Enable UI updates on property changes INotifyPropertyChanged on all ViewModels Auto-properties without notification public string Title { get => _title; set { _title = value; OnPropertyChanged(); } } public string Title { get; set; } expecting UI updates High https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
23 22 Data Binding Use ObservableCollection for lists Collection change notifications for ItemsSources ObservableCollection<T> for bound lists List<T> for data-bound collections ObservableCollection<Item> Items { get; } = new(); List<Item> Items { get; set; } = new(); High https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
24 23 Data Binding Use function bindings with x:Bind Call static methods directly in markup x:Bind to static converter methods IValueConverter for trivial transforms <TextBlock Visibility="{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}"/> Full IValueConverter class for bool to Visibility Medium https://learn.microsoft.com/en-us/windows/uwp/data-binding/function-bindings
25 24 Data Binding Specify Mode on x:Bind x:Bind defaults to OneTime not OneWay Mode=OneWay or TwoWay when live updates needed Omitting Mode and getting stale UI Text="{x:Bind Title, Mode=OneWay}" Text="{x:Bind Title}" expecting live updates High https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
26 25 Data Binding Use CollectionViewSource for grouping Group and sort collections declaratively CollectionViewSource for grouped ListView and GridView Manual grouping logic in code-behind <CollectionViewSource x:Key="GroupedItems" IsSourceGrouped="True" Source="{x:Bind GroupedData}"/> Manual loop building grouped StackPanels Medium https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
27 26 Performance Use ListView and GridView virtualization Only creates containers for visible items Default virtualization in ListView and GridView Setting ItemsPanel to non-virtualizing panel <ListView ItemsSource="{x:Bind Items}"/> (virtualizes by default) <ListView><ListView.ItemsPanel><ItemsPanelTemplate><StackPanel/></ItemsPanelTemplate></ListView.ItemsPanel></ListView> High https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview
28 27 Performance Use ISupportIncrementalLoading Load data on demand as user scrolls ISupportIncrementalLoading for large datasets Loading entire collection upfront class IncrementalSource : ObservableCollection<Item>, ISupportIncrementalLoading await LoadAll() loading 50K items at startup Medium https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
29 28 Performance Reduce XAML visual tree depth Simpler trees layout and render faster Flat templates with minimal nesting Deeply nested panels in DataTemplates <StackPanel><TextBlock/><TextBlock/></StackPanel> in item template <Grid><Border><StackPanel><Grid>... 8 levels in item template Medium https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-xaml-loading
30 29 Performance Use compiled bindings in DataTemplates x:Bind in templates requires x:DataType x:DataType on DataTemplate for compiled bindings {Binding} in item templates for large lists <DataTemplate x:DataType="local:Item"><TextBlock Text="{x:Bind Name}"/></DataTemplate> <DataTemplate><TextBlock Text="{Binding Name}"/></DataTemplate> High https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
31 30 Performance Profile with Visual Studio diagnostics Measure before optimizing Application Timeline and Memory Usage tools Guessing at performance problems VS Diagnostic Tools > Application Timeline Optimizing without profiling data Medium https://learn.microsoft.com/en-us/visualstudio/profiling/application-timeline
32 31 Threading Use async/await for all IO Keep UI thread responsive async/await for file network and database operations Synchronous IO blocking the UI thread var file = await StorageFile.GetFileFromPathAsync(path); StorageFile.GetFileFromPathAsync(path).AsTask().Result; High https://learn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
33 32 Threading Use CoreDispatcher for UI thread access Post work back to the UI thread from background Dispatcher.RunAsync from background threads Touching UI elements from background threads await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Status = "Done"); textBlock.Text = "Done" from Task.Run High https://learn.microsoft.com/en-us/uwp/api/windows.ui.core.coredispatcher
34 33 Threading Offload CPU work with Task.Run Keep compute-heavy work off UI thread Task.Run for CPU-bound operations Heavy computation blocking UI var result = await Task.Run(() => ProcessData(items)); var result = ProcessData(items); freezing UI High https://learn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
35 34 Threading Use IProgress for status updates Report progress from background operations IProgress<T> for progress reporting to UI Polling shared variables for progress var progress = new Progress<int>(p => ProgressBar.Value = p); await Task.Run(() => Process(progress)); while (!done) { await Task.Delay(100); check shared field; } Medium https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
36 35 Adaptive Use AdaptiveTrigger for responsive layouts MinWindowWidth and MinWindowHeight triggers fire at standard breakpoints (640 small / 1008 medium) AdaptiveTrigger inside VisualState.StateTriggers with the 640 and 1008 breakpoints Fixed layouts for a single screen size <VisualState.StateTriggers><AdaptiveTrigger MinWindowWidth="640"/></VisualState.StateTriggers> Single-column layout at all widths High https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
37 36 Adaptive Design for multiple device families Phone tablet desktop Xbox and HoloLens DeviceFamily-specific views and resources Desktop-only design ignoring other form factors DeviceFamily-Mobile/MainPage.xaml for phone-specific layout Fixed 1920x1080 layout Medium https://learn.microsoft.com/en-us/windows/uwp/design/layout/screen-sizes-and-breakpoints-for-responsive-design
38 37 Adaptive Use RelativePanel for adaptive positioning Controls position relative to each other RelativePanel for layouts that reflow at breakpoints Absolute positioning or fixed margins <Button RelativePanel.Below="title" RelativePanel.AlignLeftWithPanel="True"/> <Button Margin="0,60,0,0"/> calculated from title height Medium https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml#relativepanel
39 38 Adaptive Support multi-window with secondary views Open detached views with CoreApplication.CreateNewView and ApplicationViewSwitcher CreateNewView and TryShowAsStandaloneAsync for multi-document scenarios Single-window assumptions when scenarios benefit from secondary views var view = CoreApplication.CreateNewView(); await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { /* set content */ }); Modal overlay used for content that should be a separate window Medium https://learn.microsoft.com/en-us/windows/uwp/design/layout/show-multiple-views
40 39 Accessibility Set AutomationProperties Enable Narrator and screen reader support AutomationProperties.Name on all interactive controls Controls without accessible names <AppBarButton AutomationProperties.Name="Save document" Icon="Save"/> <AppBarButton Icon="Save"/> without name High https://learn.microsoft.com/en-us/windows/uwp/design/accessibility/basic-accessibility-information
41 40 Accessibility Support keyboard and gamepad All functions reachable without touch Tab navigation XYFocus and access keys Touch-only interactions <Button AccessKey="S" XYFocusDown="{x:Bind OtherButton}"/> No keyboard or gamepad support High https://learn.microsoft.com/en-us/windows/uwp/design/input/keyboard-interactions
42 41 Accessibility Support contrast themes Respect system contrast themes (renamed from high contrast in Windows 11) ThemeResource brushes that adapt to contrast themes Hardcoded colors that vanish under contrast themes Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}" Foreground="#444444" High https://learn.microsoft.com/en-us/windows/uwp/design/accessibility/high-contrast-themes
43 42 Accessibility Test with Narrator and Accessibility Insights Validate screen reader and automation compliance Regular Narrator walkthrough and Accessibility Insights scan Shipping without accessibility testing Accessibility Insights FastPass on every page No accessibility testing before release Medium https://accessibilityinsights.io/
44 43 Architecture Use MVVM pattern Separate View ViewModel and Model ViewModel with INotifyPropertyChanged and ICommand Business logic in code-behind ViewModel bound via DataContext with commands MainPage.xaml.cs with database calls and UI logic Medium https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-and-mvvm
45 44 Architecture Use Template Studio for scaffolding Proven project templates with navigation and services Windows Template Studio for new UWP projects Blank project with manual boilerplate Template Studio with MVVM Toolkit and navigation service Blank App template building everything from scratch Low https://github.com/microsoft/TemplateStudio
46 45 Architecture Use dependency injection Register services for testability Microsoft.Extensions.DI for service resolution Static singletons and manual construction services.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>(); DataService.Instance or new DataService() everywhere Medium https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
47 46 Architecture Keep platform APIs behind abstractions Isolate WinRT APIs from business logic Interfaces wrapping StorageFile FilePicker etc Direct WinRT calls in ViewModels IFileService wrapping FileOpenPicker and StorageFile FileOpenPicker usage directly in ViewModel Medium https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-and-mvvm
48 47 Lifecycle Handle suspend and resume UWP apps are suspended when not in foreground Save state in OnSuspending and restore in OnLaunched Ignoring app lifecycle losing user state Application.Current.Suspending += (s, e) => SaveState(); No suspend handler losing in-progress form data High https://learn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle
49 48 Lifecycle Use ExtendedExecutionSession for background work Request extended time for unfinished operations ExtendedExecutionSession for saving or uploads Assuming background work completes after suspend var session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.SavingData }; Long upload with no extended execution that gets killed on suspend Medium https://learn.microsoft.com/en-us/windows/uwp/launch-resume/run-minimized-with-extended-execution
50 49 Lifecycle Handle prelaunch Apps must opt in to prelaunch via CoreApplication.EnablePrelaunch(true) starting in Windows 10 1607; check LaunchActivatedEventArgs.PrelaunchActivated to skip user-visible work Opt in with EnablePrelaunch and skip heavy init when PrelaunchActivated is true Performing full initialization or navigating during prelaunch CoreApplication.EnablePrelaunch(true); if (e.PrelaunchActivated) return; // skip heavy init Loading all data and navigating on prelaunch Medium https://learn.microsoft.com/en-us/windows/uwp/launch-resume/handle-app-prelaunch
51 50 Testing Unit test ViewModels Test logic without UI framework dependencies xUnit or MSTest on ViewModel methods Testing only through the running app [Fact] public async Task Load_PopulatesItems() { await vm.LoadAsync(); Assert.NotEmpty(vm.Items); } Manual testing by tapping through the app Medium https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
52 51 Testing Use WinAppDriver with Appium for UI tests Automated UI testing for UWP (Coded UI Test was deprecated in Visual Studio 2019); WinAppDriver v1 is in low-maintenance mode and Appium 2 is the modern direction WinAppDriver with Appium for end-to-end tests Manual regression testing session.FindElementByAccessibilityId("SaveButton").Click(); Manual click-through testing before each release Medium https://github.com/microsoft/WinAppDriver
53 52 Testing Test on multiple device families Behavior varies across phone desktop and Xbox Test on device emulators and real hardware Desktop-only testing Test on Mobile emulator and Xbox dev mode Only running on local desktop Medium https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/device-portal
54 53 Architecture Prefer WinUI 3 for new projects UWP is in maintenance mode and Microsoft recommends WinUI 3 and Windows App SDK for new development WinUI 3 with Windows App SDK for new desktop apps Starting new projects on UWP when WinUI 3 is available New project with Microsoft.WindowsAppSDK and WinUI 3 New UWP project for a desktop-only app in 2024+ Medium https://learn.microsoft.com/en-us/windows/apps/get-started/
55 54 Architecture Plan migration to Windows App SDK Microsoft provides migration guides from UWP to WinUI 3 Incremental migration using XAML Islands or full port to WinUI 3 Ignoring migration path and accumulating UWP technical debt Follow UWP to WinUI 3 migration guide for existing apps Continuing major feature development on UWP without migration plan Medium https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/migrate-to-windows-app-sdk/overall-migration-strategy
56 55 Lifecycle Use a deferral when saving async state on suspend Suspending grants only ~5 seconds before the OS may terminate; await work needs SuspendingOperation.GetDeferral and Complete or save returns before it finishes GetDeferral around async save calls and Complete in finally Async work that returns the suspending handler before completion async void OnSuspending(object s, SuspendingEventArgs e) { var d = e.SuspendingOperation.GetDeferral(); try { await SaveAsync(); } finally { d.Complete(); } } async void OnSuspending(object s, SuspendingEventArgs e) { await SaveAsync(); } // handler returns before save completes Medium https://learn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle

View File

@@ -0,0 +1,50 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,Composition,Use Composition API for new projects,Composition API offers better TypeScript support and logic reuse,<script setup> for components,Options API for new projects,<script setup>,export default { data() },Medium,https://vuejs.org/guide/extras/composition-api-faq.html
2,Composition,Use script setup syntax,Cleaner syntax with automatic exports,<script setup> with defineProps,setup() function manually,<script setup>,<script> setup() { return {} },Low,https://vuejs.org/api/sfc-script-setup.html
3,Reactivity,Use ref for primitives,ref() for primitive values that need reactivity,ref() for strings numbers booleans,reactive() for primitives,const count = ref(0),const count = reactive(0),Medium,https://vuejs.org/guide/essentials/reactivity-fundamentals.html
4,Reactivity,Use reactive for objects,reactive() for complex objects and arrays,reactive() for objects with multiple properties,ref() for complex objects,const state = reactive({ user: null }),const state = ref({ user: null }),Medium,
5,Reactivity,Access ref values with .value,Remember .value in script unwrap in template,Use .value in script,Forget .value in script,count.value++,count++ (in script),High,
6,Reactivity,Use computed for derived state,Computed properties cache and update automatically,computed() for derived values,Methods for derived values,const doubled = computed(() => count.value * 2),const doubled = () => count.value * 2,Medium,https://vuejs.org/guide/essentials/computed.html
7,Reactivity,Use shallowRef for large objects,Avoid deep reactivity for performance,shallowRef for large data structures,ref for large nested objects,const bigData = shallowRef(largeObject),const bigData = ref(largeObject),Medium,https://vuejs.org/api/reactivity-advanced.html#shallowref
8,Watchers,Use watchEffect for simple cases,Auto-tracks dependencies,watchEffect for simple reactive effects,watch with explicit deps when not needed,watchEffect(() => console.log(count.value)),"watch(count, (val) => console.log(val))",Low,https://vuejs.org/guide/essentials/watchers.html
9,Watchers,Use watch for specific sources,Explicit control over what to watch,watch with specific refs,watchEffect for complex conditional logic,"watch(userId, fetchUser)",watchEffect with conditionals,Medium,
10,Watchers,Clean up side effects,Return cleanup function in watchers,Return cleanup in watchEffect,Leave subscriptions open,watchEffect((onCleanup) => { onCleanup(unsub) }),watchEffect without cleanup,High,
11,Props,Define props with defineProps,Type-safe prop definitions,defineProps with TypeScript,Props without types,defineProps<{ msg: string }>(),defineProps(['msg']),Medium,https://vuejs.org/guide/typescript/composition-api.html#typing-component-props
12,Props,Use withDefaults for default values,Provide defaults for optional props,withDefaults with defineProps,Defaults in destructuring,"withDefaults(defineProps<Props>(), { count: 0 })",const { count = 0 } = defineProps(),Medium,
13,Props,Avoid mutating props,Props should be read-only,Emit events to parent for changes,Direct prop mutation,"emit('update:modelValue', newVal)",props.modelValue = newVal,High,
14,Emits,Define emits with defineEmits,Type-safe event emissions,defineEmits with types,Emit without definition,defineEmits<{ change: [id: number] }>(),"emit('change', id) without define",Medium,https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits
15,Emits,Use v-model for two-way binding,Simplified parent-child data flow,v-model with modelValue prop,:value + @input manually,"<Child v-model=""value""/>","<Child :value=""value"" @input=""value = $event""/>",Low,https://vuejs.org/guide/components/v-model.html
16,Lifecycle,Use onMounted for DOM access,DOM is ready in onMounted,onMounted for DOM operations,Access DOM in setup directly,onMounted(() => el.value.focus()),el.value.focus() in setup,High,https://vuejs.org/api/composition-api-lifecycle.html
17,Lifecycle,Clean up in onUnmounted,Remove listeners and subscriptions,onUnmounted for cleanup,Leave listeners attached,onUnmounted(() => window.removeEventListener()),No cleanup on unmount,High,
18,Lifecycle,Avoid onBeforeMount for data,Use onMounted or setup for data fetching,Fetch in onMounted or setup,Fetch in onBeforeMount,onMounted(async () => await fetchData()),onBeforeMount(async () => await fetchData()),Low,
19,Components,Use single-file components,Keep template script style together,.vue files for components,Separate template/script files,Component.vue with all parts,Component.js + Component.html,Low,
20,Components,Use PascalCase for components,Consistent component naming,PascalCase in imports and templates,kebab-case in script,<MyComponent/>,<my-component/>,Low,https://vuejs.org/style-guide/rules-strongly-recommended.html
21,Components,Prefer composition over mixins,Composables replace mixins,Composables for shared logic,Mixins for code reuse,const { data } = useApi(),mixins: [apiMixin],Medium,
22,Composables,Name composables with use prefix,Convention for composable functions,useFetch useAuth useForm,getData or fetchApi,export function useFetch(),export function fetchData(),Medium,https://vuejs.org/guide/reusability/composables.html
23,Composables,Return refs from composables,Maintain reactivity when destructuring,Return ref values,Return reactive objects that lose reactivity,return { data: ref(null) },return reactive({ data: null }),Medium,
24,Composables,Accept ref or value params,Use toValue for flexible inputs,toValue() or unref() for params,Only accept ref or only value,const val = toValue(maybeRef),const val = maybeRef.value,Low,https://vuejs.org/api/reactivity-utilities.html#tovalue
25,Templates,Use v-bind shorthand,Cleaner template syntax,:prop instead of v-bind:prop,Full v-bind syntax,"<div :class=""cls"">","<div v-bind:class=""cls"">",Low,
26,Templates,Use v-on shorthand,Cleaner event binding,@event instead of v-on:event,Full v-on syntax,"<button @click=""handler"">","<button v-on:click=""handler"">",Low,
27,Templates,Avoid v-if with v-for,v-if has higher priority causes issues,Wrap in template or computed filter,v-if on same element as v-for,<template v-for><div v-if>,<div v-for v-if>,High,https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for
28,Templates,Use key with v-for,Proper list rendering and updates,Unique key for each item,Index as key for dynamic lists,"v-for=""item in items"" :key=""item.id""","v-for=""(item, i) in items"" :key=""i""",High,
29,State,Use Pinia for global state,Official state management for Vue 3,Pinia stores for shared state,Vuex for new projects,const store = useCounterStore(),Vuex with mutations,Medium,https://pinia.vuejs.org/
30,State,Define stores with defineStore,Composition API style stores,Setup stores with defineStore,Options stores for complex state,"defineStore('counter', () => {})","defineStore('counter', { state })",Low,
31,State,Use storeToRefs for destructuring,Maintain reactivity when destructuring,storeToRefs(store),Direct destructuring,const { count } = storeToRefs(store),const { count } = store,High,https://pinia.vuejs.org/core-concepts/#destructuring-from-a-store
32,Routing,Use useRouter and useRoute,Composition API router access,useRouter() useRoute() in setup,this.$router this.$route,const router = useRouter(),this.$router.push(),Medium,https://router.vuejs.org/guide/advanced/composition-api.html
33,Routing,Lazy load route components,Code splitting for routes,() => import() for components,Static imports for all routes,component: () => import('./Page.vue'),component: Page,Medium,https://router.vuejs.org/guide/advanced/lazy-loading.html
34,Routing,Use navigation guards,Protect routes and handle redirects,beforeEach for auth checks,Check auth in each component,router.beforeEach((to) => {}),Check auth in onMounted,Medium,
35,Performance,Use v-once for static content,Skip re-renders for static elements,v-once on never-changing content,v-once on dynamic content,<div v-once>{{ staticText }}</div>,<div v-once>{{ dynamicText }}</div>,Low,https://vuejs.org/api/built-in-directives.html#v-once
36,Performance,Use v-memo for expensive lists,Memoize list items,v-memo with dependency array,Re-render entire list always,"<div v-for v-memo=""[item.id]"">",<div v-for> without memo,Medium,https://vuejs.org/api/built-in-directives.html#v-memo
37,Performance,Use shallowReactive for flat objects,Avoid deep reactivity overhead,shallowReactive for flat state,reactive for simple objects,shallowReactive({ count: 0 }),reactive({ count: 0 }),Low,
38,Performance,Use defineAsyncComponent,Lazy load heavy components,defineAsyncComponent for modals dialogs,Import all components eagerly,defineAsyncComponent(() => import()),import HeavyComponent from,Medium,https://vuejs.org/guide/components/async.html
39,TypeScript,Use generic components,Type-safe reusable components,Generic with defineComponent,Any types in components,"<script setup lang=""ts"" generic=""T"">",<script setup> without types,Medium,https://vuejs.org/guide/typescript/composition-api.html
40,TypeScript,Type template refs,Proper typing for DOM refs,ref<HTMLInputElement>(null),ref(null) without type,const input = ref<HTMLInputElement>(null),const input = ref(null),Medium,
41,TypeScript,Use PropType for complex props,Type complex prop types,PropType<User> for object props,Object without type,type: Object as PropType<User>,type: Object,Medium,
42,Testing,Use Vue Test Utils,Official testing library,mount shallowMount for components,Manual DOM testing,import { mount } from '@vue/test-utils',document.createElement,Medium,https://test-utils.vuejs.org/
43,Testing,Test component behavior,Focus on inputs and outputs,Test props emit and rendered output,Test internal implementation,expect(wrapper.text()).toContain(),expect(wrapper.vm.internalState),Medium,
44,Forms,Use v-model modifiers,Built-in input handling,.lazy .number .trim modifiers,Manual input parsing,"<input v-model.number=""age"">","<input v-model=""age""> then parse",Low,https://vuejs.org/guide/essentials/forms.html#modifiers
45,Forms,Use VeeValidate or FormKit,Form validation libraries,VeeValidate for complex forms,Manual validation logic,useField useForm from vee-validate,Custom validation in each input,Medium,
46,Accessibility,Use semantic elements,Proper HTML elements in templates,button nav main for purpose,div for everything,<button @click>,<div @click>,High,
47,Accessibility,Bind aria attributes dynamically,Keep ARIA in sync with state,":aria-expanded=""isOpen""",Static ARIA values,":aria-expanded=""menuOpen""","aria-expanded=""true""",Medium,
48,SSR,Use Nuxt for SSR,Full-featured SSR framework,Nuxt 3 for SSR apps,Manual SSR setup,npx nuxi init my-app,Custom SSR configuration,Medium,https://nuxt.com/
49,SSR,Handle hydration mismatches,Client/server content must match,ClientOnly for browser-only content,Different content server/client,<ClientOnly><BrowserWidget/></ClientOnly>,<div>{{ Date.now() }}</div>,High,
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 Composition Use Composition API for new projects Composition API offers better TypeScript support and logic reuse <script setup> for components Options API for new projects <script setup> export default { data() } Medium https://vuejs.org/guide/extras/composition-api-faq.html
3 2 Composition Use script setup syntax Cleaner syntax with automatic exports <script setup> with defineProps setup() function manually <script setup> <script> setup() { return {} } Low https://vuejs.org/api/sfc-script-setup.html
4 3 Reactivity Use ref for primitives ref() for primitive values that need reactivity ref() for strings numbers booleans reactive() for primitives const count = ref(0) const count = reactive(0) Medium https://vuejs.org/guide/essentials/reactivity-fundamentals.html
5 4 Reactivity Use reactive for objects reactive() for complex objects and arrays reactive() for objects with multiple properties ref() for complex objects const state = reactive({ user: null }) const state = ref({ user: null }) Medium
6 5 Reactivity Access ref values with .value Remember .value in script unwrap in template Use .value in script Forget .value in script count.value++ count++ (in script) High
7 6 Reactivity Use computed for derived state Computed properties cache and update automatically computed() for derived values Methods for derived values const doubled = computed(() => count.value * 2) const doubled = () => count.value * 2 Medium https://vuejs.org/guide/essentials/computed.html
8 7 Reactivity Use shallowRef for large objects Avoid deep reactivity for performance shallowRef for large data structures ref for large nested objects const bigData = shallowRef(largeObject) const bigData = ref(largeObject) Medium https://vuejs.org/api/reactivity-advanced.html#shallowref
9 8 Watchers Use watchEffect for simple cases Auto-tracks dependencies watchEffect for simple reactive effects watch with explicit deps when not needed watchEffect(() => console.log(count.value)) watch(count, (val) => console.log(val)) Low https://vuejs.org/guide/essentials/watchers.html
10 9 Watchers Use watch for specific sources Explicit control over what to watch watch with specific refs watchEffect for complex conditional logic watch(userId, fetchUser) watchEffect with conditionals Medium
11 10 Watchers Clean up side effects Return cleanup function in watchers Return cleanup in watchEffect Leave subscriptions open watchEffect((onCleanup) => { onCleanup(unsub) }) watchEffect without cleanup High
12 11 Props Define props with defineProps Type-safe prop definitions defineProps with TypeScript Props without types defineProps<{ msg: string }>() defineProps(['msg']) Medium https://vuejs.org/guide/typescript/composition-api.html#typing-component-props
13 12 Props Use withDefaults for default values Provide defaults for optional props withDefaults with defineProps Defaults in destructuring withDefaults(defineProps<Props>(), { count: 0 }) const { count = 0 } = defineProps() Medium
14 13 Props Avoid mutating props Props should be read-only Emit events to parent for changes Direct prop mutation emit('update:modelValue', newVal) props.modelValue = newVal High
15 14 Emits Define emits with defineEmits Type-safe event emissions defineEmits with types Emit without definition defineEmits<{ change: [id: number] }>() emit('change', id) without define Medium https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits
16 15 Emits Use v-model for two-way binding Simplified parent-child data flow v-model with modelValue prop :value + @input manually <Child v-model="value"/> <Child :value="value" @input="value = $event"/> Low https://vuejs.org/guide/components/v-model.html
17 16 Lifecycle Use onMounted for DOM access DOM is ready in onMounted onMounted for DOM operations Access DOM in setup directly onMounted(() => el.value.focus()) el.value.focus() in setup High https://vuejs.org/api/composition-api-lifecycle.html
18 17 Lifecycle Clean up in onUnmounted Remove listeners and subscriptions onUnmounted for cleanup Leave listeners attached onUnmounted(() => window.removeEventListener()) No cleanup on unmount High
19 18 Lifecycle Avoid onBeforeMount for data Use onMounted or setup for data fetching Fetch in onMounted or setup Fetch in onBeforeMount onMounted(async () => await fetchData()) onBeforeMount(async () => await fetchData()) Low
20 19 Components Use single-file components Keep template script style together .vue files for components Separate template/script files Component.vue with all parts Component.js + Component.html Low
21 20 Components Use PascalCase for components Consistent component naming PascalCase in imports and templates kebab-case in script <MyComponent/> <my-component/> Low https://vuejs.org/style-guide/rules-strongly-recommended.html
22 21 Components Prefer composition over mixins Composables replace mixins Composables for shared logic Mixins for code reuse const { data } = useApi() mixins: [apiMixin] Medium
23 22 Composables Name composables with use prefix Convention for composable functions useFetch useAuth useForm getData or fetchApi export function useFetch() export function fetchData() Medium https://vuejs.org/guide/reusability/composables.html
24 23 Composables Return refs from composables Maintain reactivity when destructuring Return ref values Return reactive objects that lose reactivity return { data: ref(null) } return reactive({ data: null }) Medium
25 24 Composables Accept ref or value params Use toValue for flexible inputs toValue() or unref() for params Only accept ref or only value const val = toValue(maybeRef) const val = maybeRef.value Low https://vuejs.org/api/reactivity-utilities.html#tovalue
26 25 Templates Use v-bind shorthand Cleaner template syntax :prop instead of v-bind:prop Full v-bind syntax <div :class="cls"> <div v-bind:class="cls"> Low
27 26 Templates Use v-on shorthand Cleaner event binding @event instead of v-on:event Full v-on syntax <button @click="handler"> <button v-on:click="handler"> Low
28 27 Templates Avoid v-if with v-for v-if has higher priority causes issues Wrap in template or computed filter v-if on same element as v-for <template v-for><div v-if> <div v-for v-if> High https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for
29 28 Templates Use key with v-for Proper list rendering and updates Unique key for each item Index as key for dynamic lists v-for="item in items" :key="item.id" v-for="(item, i) in items" :key="i" High
30 29 State Use Pinia for global state Official state management for Vue 3 Pinia stores for shared state Vuex for new projects const store = useCounterStore() Vuex with mutations Medium https://pinia.vuejs.org/
31 30 State Define stores with defineStore Composition API style stores Setup stores with defineStore Options stores for complex state defineStore('counter', () => {}) defineStore('counter', { state }) Low
32 31 State Use storeToRefs for destructuring Maintain reactivity when destructuring storeToRefs(store) Direct destructuring const { count } = storeToRefs(store) const { count } = store High https://pinia.vuejs.org/core-concepts/#destructuring-from-a-store
33 32 Routing Use useRouter and useRoute Composition API router access useRouter() useRoute() in setup this.$router this.$route const router = useRouter() this.$router.push() Medium https://router.vuejs.org/guide/advanced/composition-api.html
34 33 Routing Lazy load route components Code splitting for routes () => import() for components Static imports for all routes component: () => import('./Page.vue') component: Page Medium https://router.vuejs.org/guide/advanced/lazy-loading.html
35 34 Routing Use navigation guards Protect routes and handle redirects beforeEach for auth checks Check auth in each component router.beforeEach((to) => {}) Check auth in onMounted Medium
36 35 Performance Use v-once for static content Skip re-renders for static elements v-once on never-changing content v-once on dynamic content <div v-once>{{ staticText }}</div> <div v-once>{{ dynamicText }}</div> Low https://vuejs.org/api/built-in-directives.html#v-once
37 36 Performance Use v-memo for expensive lists Memoize list items v-memo with dependency array Re-render entire list always <div v-for v-memo="[item.id]"> <div v-for> without memo Medium https://vuejs.org/api/built-in-directives.html#v-memo
38 37 Performance Use shallowReactive for flat objects Avoid deep reactivity overhead shallowReactive for flat state reactive for simple objects shallowReactive({ count: 0 }) reactive({ count: 0 }) Low
39 38 Performance Use defineAsyncComponent Lazy load heavy components defineAsyncComponent for modals dialogs Import all components eagerly defineAsyncComponent(() => import()) import HeavyComponent from Medium https://vuejs.org/guide/components/async.html
40 39 TypeScript Use generic components Type-safe reusable components Generic with defineComponent Any types in components <script setup lang="ts" generic="T"> <script setup> without types Medium https://vuejs.org/guide/typescript/composition-api.html
41 40 TypeScript Type template refs Proper typing for DOM refs ref<HTMLInputElement>(null) ref(null) without type const input = ref<HTMLInputElement>(null) const input = ref(null) Medium
42 41 TypeScript Use PropType for complex props Type complex prop types PropType<User> for object props Object without type type: Object as PropType<User> type: Object Medium
43 42 Testing Use Vue Test Utils Official testing library mount shallowMount for components Manual DOM testing import { mount } from '@vue/test-utils' document.createElement Medium https://test-utils.vuejs.org/
44 43 Testing Test component behavior Focus on inputs and outputs Test props emit and rendered output Test internal implementation expect(wrapper.text()).toContain() expect(wrapper.vm.internalState) Medium
45 44 Forms Use v-model modifiers Built-in input handling .lazy .number .trim modifiers Manual input parsing <input v-model.number="age"> <input v-model="age"> then parse Low https://vuejs.org/guide/essentials/forms.html#modifiers
46 45 Forms Use VeeValidate or FormKit Form validation libraries VeeValidate for complex forms Manual validation logic useField useForm from vee-validate Custom validation in each input Medium
47 46 Accessibility Use semantic elements Proper HTML elements in templates button nav main for purpose div for everything <button @click> <div @click> High
48 47 Accessibility Bind aria attributes dynamically Keep ARIA in sync with state :aria-expanded="isOpen" Static ARIA values :aria-expanded="menuOpen" aria-expanded="true" Medium
49 48 SSR Use Nuxt for SSR Full-featured SSR framework Nuxt 3 for SSR apps Manual SSR setup npx nuxi init my-app Custom SSR configuration Medium https://nuxt.com/
50 49 SSR Handle hydration mismatches Client/server content must match ClientOnly for browser-only content Different content server/client <ClientOnly><BrowserWidget/></ClientOnly> <div>{{ Date.now() }}</div> High

View File

@@ -0,0 +1,60 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,XAML,Use x:Bind for compiled bindings,Compile-time checked bindings with better performance,x:Bind for type-safe bindings,{Binding} when x:Bind works,"<TextBlock Text=""{x:Bind ViewModel.Title, Mode=OneWay}""/>","<TextBlock Text=""{Binding Title}""/>",High,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
2,XAML,Use x:Load for deferred loading,Only instantiate UI elements when needed,x:Load=False for hidden panels and dialogs,Loading all UI upfront,"<StackPanel x:Load=""{x:Bind ShowDetails, Mode=OneWay}"">","Always-loaded collapsed panels",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-load-attribute
3,XAML,Use x:Phase for incremental rendering,Load list items in phases for smooth scrolling,x:Phase on secondary content in DataTemplates,Loading all template content in phase 0,"<TextBlock x:Phase=""1"" Text=""{x:Bind Description}""/>","All content in single phase for complex templates",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
4,XAML,Use x:DefaultBindMode,Set default binding mode for a scope,x:DefaultBindMode=OneWay on containers with many bindings,Mode=OneWay on every individual x:Bind,"<StackPanel x:DefaultBindMode=""OneWay"">","Mode=OneWay repeated on 20 bindings",Low,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
5,Controls,Use NavigationView for app navigation,WinUI 3 NavigationView with Left Top and LeftCompact display modes plus footer items,NavigationView with PaneDisplayMode for main app shell,Custom hamburger menu implementation,"<NavigationView><NavigationView.MenuItems><NavigationViewItem Content=""Home""/></NavigationView.MenuItems></NavigationView>","Custom SplitView with manual hamburger button",High,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/navigationview
6,Controls,Use InfoBar for status messages,Non-intrusive informational messages,InfoBar for success warning and error messages,Custom styled StackPanel for status,"<InfoBar IsOpen=""True"" Severity=""Warning"" Title=""Update available""/>","<StackPanel Background=""Yellow""><TextBlock Text=""Warning""/></StackPanel>",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/infobar
7,Controls,Use TeachingTip for onboarding,Contextual tips attached to UI elements,TeachingTip for feature discovery,Custom popup for teaching,"<TeachingTip Target=""{x:Bind SearchBox}"" Title=""Try searching""/>","Custom Popup positioned near target element",Low,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/teaching-tip
8,Controls,Use ContentDialog for modal interactions,Standard modal dialog pattern,ContentDialog for confirmations and input,Custom overlay Panel as dialog,"<ContentDialog Title=""Delete?"" PrimaryButtonText=""Delete"" CloseButtonText=""Cancel""/>","Grid overlay with manual focus trapping",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/dialogs
9,Controls,Use BreadcrumbBar for hierarchy,Show navigation path in hierarchical apps,BreadcrumbBar for folder or category navigation,Manual TextBlock breadcrumb chain,"<BreadcrumbBar ItemsSource=""{x:Bind Breadcrumbs}""/>","<StackPanel Orientation=""Horizontal""><TextBlock Text=""Home > Settings > Display""/></StackPanel>",Low,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/breadcrumbbar
10,Styling,Use Lightweight Styling,Override control sub-properties via resources,Lightweight styling resource keys to tweak controls,Full ControlTemplate override for small changes,"<Button><Button.Resources><ResourceDictionary><ResourceDictionary.ThemeDictionaries><ResourceDictionary x:Key=""Light""><SolidColorBrush x:Key=""ButtonBackground"" Color=""MediumSlateBlue""/></ResourceDictionary></ResourceDictionary.ThemeDictionaries></ResourceDictionary></Button.Resources></Button>","Full ControlTemplate copy to change background color",High,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-styles#lightweight-styling
11,Styling,Use WinUI theme resources,Consistent Fluent Design colors and brushes,WinUI theme resource keys for colors,Hardcoded hex color values,"Background=""{ThemeResource CardBackgroundFillColorDefaultBrush}""","Background=""#FF2D2D30""",High,https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/color
12,Styling,Support light and dark themes,Respect user and system theme preference,ThemeResource for theme-adaptive values,Hardcoded colors that break in dark mode,"Foreground=""{ThemeResource TextFillColorPrimaryBrush}""","Foreground=""Black""",High,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-theme-resources
13,Styling,Use Fluent Design system,Acrylic Mica Reveal and rounded corners,Built-in Fluent materials and effects,Custom blur and shadow implementations,"<Grid Background=""{ThemeResource AcrylicInAppFillColorDefaultBrush}""/>","Custom CompositionBrush recreating acrylic",Medium,https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials
14,Navigation,Use Frame for page navigation,Microsoft.UI.Xaml.Controls.Frame for WinUI 3 page navigation,Frame.Navigate with page types and parameters,Swapping UserControls in a ContentControl,"rootFrame.Navigate(typeof(SettingsPage), parameter);","contentArea.Content = new SettingsControl();",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages
15,Navigation,Pass typed navigation parameters,Type-safe data passing between pages,Typed parameter in OnNavigatedTo,Dictionary or string parsing for parameters,"protected override void OnNavigatedTo(NavigationEventArgs e) { var item = (Item)e.Parameter; }","var id = int.Parse(e.Parameter.ToString());",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages
16,Navigation,Handle back navigation,WinUI 3 uses NavigationView.BackRequested instead of UWP SystemNavigationManager,Register NavigationView.BackRequested handler and manage back stack,"Ignoring back navigation","navigationView.BackRequested += OnBackRequested;","No back button support",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigation-history-and-backwards-navigation
17,Navigation,Use deep linking,Handle protocol activation so URIs route to the right page,Register protocol then check ExtendedActivationKind.Protocol on activation,Single entry point ignoring activation context,"AppInstance.GetCurrent().GetActivatedEventArgs() with ExtendedActivationKind.Protocol","Ignoring activation arguments",Medium,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation
18,Data Binding,Use ObservableCollection for lists,Notifies UI of collection changes,ObservableCollection<T> for bound ItemsSources,List<T> for bound collections,"ObservableCollection<Item> Items { get; } = new();","List<Item> Items { get; set; } = new();",High,https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
19,Data Binding,Use INotifyPropertyChanged,Enable property change notification for UI updates,INotifyPropertyChanged on ViewModels,Properties without notification,"public string Name { get => _name; set => SetProperty(ref _name, value); }","public string Name { get; set; } without notification",High,https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
20,Data Binding,Use function binding with x:Bind,Call methods directly in bindings,x:Bind with method references for transforms,IValueConverter for simple logic,"<TextBlock Visibility=""{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}""/>","IValueConverter class for bool to visibility",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/function-bindings
21,Data Binding,Specify Mode explicitly on x:Bind,x:Bind defaults to OneTime not OneWay,Mode=OneWay or Mode=TwoWay when updates needed,Forgetting Mode and getting stale UI,"Text=""{x:Bind Title, Mode=OneWay}""","Text=""{x:Bind Title}"" expecting live updates",High,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
22,Performance,Use ItemsRepeater for custom lists,Virtualizing layout with full control,ItemsRepeater for custom list layouts,ListView for highly customized item layouts,"<ItemsRepeater ItemsSource=""{x:Bind Items}""><ItemsRepeater.Layout><StackLayout/></ItemsRepeater.Layout></ItemsRepeater>","ListView with heavily modified template and removed chrome",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/items-repeater
23,Performance,Use incremental loading,Load data on demand as user scrolls,ISupportIncrementalLoading for large data sets,Loading entire dataset upfront,"class IncrementalItemSource : ObservableCollection<Item>, ISupportIncrementalLoading","await LoadAllItems() on page load for 10K items",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
24,Performance,Reduce visual tree complexity,Simpler trees render faster,Minimal nesting in DataTemplates,Deeply nested panels in item templates,"<StackPanel><TextBlock/><TextBlock/></StackPanel>","<Grid><Border><StackPanel><Grid>... 8 levels deep",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/performance/optimize-xaml-loading
25,Performance,Use compiled bindings over reflection,x:Bind generates code at compile time,x:Bind for hot paths and list items,{Binding} in DataTemplates and frequently updated UI,"<DataTemplate x:DataType=""local:Item""><TextBlock Text=""{x:Bind Name}""/></DataTemplate>","<DataTemplate><TextBlock Text=""{Binding Name}""/></DataTemplate>",High,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
26,Threading,Use DispatcherQueue not Dispatcher,WinUI 3 uses Microsoft.UI.Dispatching.DispatcherQueue instead of UWP CoreDispatcher,DispatcherQueue.TryEnqueue for UI thread access,Dispatcher.RunAsync (UWP pattern),"_dispatcherQueue.TryEnqueue(() => Status = ""Done"");","Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ...);",High,https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue
27,Threading,Use async/await for IO operations,Keep UI responsive during file and network access,async/await for IO so the UI thread keeps rendering,Synchronous IO on UI thread,"var data = await httpClient.GetStringAsync(url);","var data = httpClient.GetStringAsync(url).Result;",High,https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
28,Threading,Use Task.Run for CPU-bound work,Offload compute to thread pool,Task.Run for heavy computation,Long-running CPU work on UI thread,"var result = await Task.Run(() => ProcessLargeDataSet());","var result = ProcessLargeDataSet(); blocking UI",High,https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming
29,Packaging,Use WinAppSDK correctly,Windows App SDK provides the runtime,WinAppSDK NuGet package and WindowsAppSDK bootstrapper,Mixing UWP and WinUI 3 APIs,"<PackageReference Include=""Microsoft.WindowsAppSDK""/>","Using Windows.UI.Xaml instead of Microsoft.UI.Xaml",High,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/
30,Packaging,Use unpackaged or packaged appropriately,Choose deployment model for your scenario,Packaged (MSIX) for Store distribution,Unpackaged without considering API limitations,"<WindowsPackageType>None</WindowsPackageType> for unpackaged","Assuming all APIs work in unpackaged mode",Medium,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/deploy-packaged-apps
31,Packaging,Use single-project MSIX,Simplified packaging for single app,Single-project MSIX packaging,Separate WAP project when not needed,"<EnableMsixTooling>true</EnableMsixTooling> in csproj","Separate Windows Application Packaging project for simple apps",Low,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/single-project-msix
32,Accessibility,Set AutomationProperties,Enable Narrator and screen reader support,AutomationProperties.Name on all interactive controls,Controls without accessible names,"<Button AutomationProperties.Name=""Save document""><FontIcon Glyph=""&#xE74E;""/></Button>","<Button><FontIcon Glyph=""&#xE74E;""/></Button> without name",High,https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information
33,Accessibility,Support keyboard navigation,Full keyboard accessibility,Tab navigation and access keys for all controls,Mouse-only interactions,"<Button AccessKey=""S"" Content=""Save""/>","Interactive elements unreachable by keyboard",High,https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-interactions
34,Accessibility,Use proper heading levels,Screen readers use headings for navigation,AutomationProperties.HeadingLevel on section headers,All text at same heading level,"<TextBlock AutomationProperties.HeadingLevel=""Level1"" Text=""Settings""/>","<TextBlock Style=""{StaticResource TitleTextBlockStyle}""/> without heading level",Medium,https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information
35,Accessibility,Support high contrast,Respect system high contrast settings,ThemeResource brushes that adapt to high contrast,Hardcoded colors ignoring high contrast,"Foreground=""{ThemeResource TextFillColorPrimaryBrush}""","Foreground=""#333333""",High,https://learn.microsoft.com/en-us/windows/apps/design/accessibility/high-contrast-themes
36,Accessibility,Test with Accessibility Insights,Validate accessibility compliance,Accessibility Insights for Windows scanning,Manual accessibility checking only,"Run Accessibility Insights FastPass on every page","Ship without accessibility testing",Medium,https://accessibilityinsights.io/
37,Architecture,Use MVVM with CommunityToolkit,Source generators reduce boilerplate,[ObservableProperty] and [RelayCommand] attributes,Manual INotifyPropertyChanged and ICommand,"[ObservableProperty] private string _title; [RelayCommand] private void Save() { }","Full INotifyPropertyChanged implementation per property",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
38,Architecture,Use dependency injection,Register services with Microsoft.Extensions.DI,IServiceProvider for ViewModel and service resolution,new ViewModel() and new Service() everywhere,"services.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>();","new MainViewModel(new DataService()) in code-behind",Medium,https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview
39,Architecture,Use Template Studio patterns,Start with proven architectural templates,Template Studio for WinUI 3 project scaffolding,Blank project with manual setup for complex apps,"WinUI 3 Template Studio with MVVM and navigation","Blank App template for production app",Low,https://github.com/microsoft/TemplateStudio
40,Architecture,Separate platform from business logic,Keep business logic in .NET Standard or shared libraries,Business logic in separate class library,Business logic mixed with WinUI types,"Shared.Core project with no WinUI references","ViewModel importing Microsoft.UI.Xaml types",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
41,Architecture,Use WinUI 3 Window management,Proper window lifecycle management,AppWindow API for multi-window scenarios,Single Window assumption in complex apps,"var appWindow = this.AppWindow;","Relying solely on MainWindow for everything",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows
42,Testing,Unit test ViewModels,Test logic independent of UI framework,xUnit or MSTest on ViewModel properties and commands,Testing through UI only,"[Fact] public async Task LoadItems_PopulatesCollection() { await vm.LoadCommand.ExecuteAsync(null); Assert.NotEmpty(vm.Items); }","Manual testing by running the app",Medium,https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
43,Testing,Use WinAppDriver for UI tests,Automated UI testing for WinUI 3,WinAppDriver or Appium for end-to-end tests,Manual regression testing,"var element = session.FindElementByAccessibilityId(""SaveButton""); element.Click();","Click-through manual testing",Medium,https://github.com/microsoft/WinAppDriver
44,Testing,Mock WinRT APIs in tests,Isolate tests from platform dependencies,Interface wrappers around WinRT APIs,Direct WinRT API calls in testable code,"IFileService wrapping StorageFile APIs","StorageFile.GetFileFromPathAsync directly in ViewModel",Medium,https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
45,Controls,Use NumberBox for numeric input,Built-in numeric entry with validation formatting and spin buttons,NumberBox with Minimum Maximum and SpinButtonPlacementMode,TextBox with manual numeric parsing and validation,"<NumberBox Value=""{x:Bind Quantity, Mode=TwoWay}"" Minimum=""0"" Maximum=""100"" SpinButtonPlacementMode=""Inline""/>","TextBox with regex validation for numbers",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/number-box
46,Controls,Use Expander for collapsible sections,Expandable content area with header for progressive disclosure,Expander for settings groups and optional content,Manual visibility toggling with buttons,"<Expander Header=""Advanced Settings""><StackPanel><ToggleSwitch Header=""Debug mode""/></StackPanel></Expander>","Button toggling StackPanel.Visibility for collapsible content",Low,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/expander
47,Controls,Use ProgressRing and ProgressBar for loading,Built-in loading indicators for determinate and indeterminate states,ProgressRing for indeterminate and ProgressBar for determinate progress,Custom spinning animation or text-based loading indicators,"<ProgressRing IsActive=""{x:Bind IsLoading, Mode=OneWay}""/>","<TextBlock Text=""Loading..."" Visibility=""{x:Bind IsLoading}""/>",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/progress-controls
48,Layout,Use VisualStateManager for responsive layouts,Adapt UI layout to window size using adaptive triggers,AdaptiveTrigger with MinWindowWidth for responsive breakpoints,Fixed layouts that break at different window sizes,"<VisualState><VisualState.StateTriggers><AdaptiveTrigger MinWindowWidth=""720""/></VisualState.StateTriggers><VisualState.Setters><Setter Target=""sidebar.Visibility"" Value=""Visible""/></VisualState.Setters></VisualState>","Fixed two-column layout at all window sizes",High,https://learn.microsoft.com/en-us/windows/apps/develop/ui/layouts-with-xaml
49,Lifecycle,Handle app activation and launch,WinUI 3 apps receive activation events for URI and notification launches,Check LaunchActivatedEventArgs in OnLaunched for activation context,Ignoring activation arguments losing deep link context,"protected override void OnLaunched(LaunchActivatedEventArgs args) { if (args.Arguments.Contains(""settings"")) NavigateToSettings(); }","Empty OnLaunched ignoring all activation parameters",Medium,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation
50,Lifecycle,Use single instancing with AppInstance,Prevent multiple app windows competing for resources,AppInstance.FindOrRegisterForKey for single-instance enforcement,Multiple instances with conflicting state,"var instance = AppInstance.FindOrRegisterForKey(""main""); if (!instance.IsCurrent) { await instance.RedirectActivationToAsync(args); }","No instance management allowing duplicate windows",Medium,https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-instancing
51,Lifecycle,Save and restore app state,Persist UI state across app restarts for continuity (ApplicationData APIs require packaged apps; unpackaged apps must use file IO or registry),Save state to local settings on window close or navigation,Losing user context on every restart,"ApplicationData.Current.LocalSettings.Values[""lastPage""] = currentPage;","No state persistence losing navigation position on restart",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/data/store-and-retrieve-app-data
52,Styling,Choose Mica vs Acrylic by surface lifetime,Mica is for long-lived primary surfaces like main windows; Acrylic is for transient light-dismiss surfaces like flyouts and context menus,Mica on root window backgrounds and Acrylic on flyouts and overlays,Acrylic on the main window or Mica on transient flyouts,"<Window.SystemBackdrop><MicaBackdrop/></Window.SystemBackdrop> ... <FlyoutPresenter Background=""{ThemeResource AcrylicInAppFillColorDefaultBrush}""/>","Acrylic on every Window background causing battery and perf cost",Medium,https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials
53,Styling,Set SystemBackdrop on Window directly,WinUI 3 1.3+ exposes Window.SystemBackdrop with MicaBackdrop and DesktopAcrylicBackdrop classes replacing manual MicaController plumbing,Window.SystemBackdrop in XAML or code,Hand-rolled MicaController wiring when SystemBackdrop API is available,"<Window.SystemBackdrop><MicaBackdrop Kind=""Base""/></Window.SystemBackdrop>","var ctrl = new Microsoft.UI.Composition.SystemBackdrops.MicaController(); ctrl.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/system-backdrops
54,Architecture,Open secondary windows with new Window,WinUI 3 supports multiple top-level windows; each Window owns an AppWindow accessible via Window.AppWindow for size and position control,new Window().Activate() for secondary windows tracking them in App state,Faking multi-window via main-window content swaps or ContentDialog,"var settings = new SettingsWindow(); settings.Activate();","MainWindow.Content = new SettingsView(); when a separate window is needed",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows
55,Architecture,Extend client area into the title bar,Use Window.ExtendsContentIntoTitleBar with SetTitleBar to host custom XAML in the chrome while preserving caption buttons,ExtendsContentIntoTitleBar=true plus SetTitleBar(element) for custom drag region,Hardcoded chrome height or custom caption buttons that break with theme and size changes,"this.ExtendsContentIntoTitleBar = true; this.SetTitleBar(AppTitleBar);","Padding=""0,32,0,0"" to reserve space without SetTitleBar leaves window non-draggable",Medium,https://learn.microsoft.com/en-us/windows/apps/develop/title-bar
56,Accessibility,Use KeyboardAccelerator for shortcuts,Map Ctrl/Alt/Shift combinations to commands using KeyboardAccelerator on UIElement,KeyboardAccelerator with Modifiers and Key on relevant controls,Manual KeyDown handlers swallowing shortcuts,"<Button Command=""{x:Bind SaveCommand}""><Button.KeyboardAccelerators><KeyboardAccelerator Modifiers=""Control"" Key=""S""/></Button.KeyboardAccelerators></Button>","Window.PreviewKeyDown=""OnKeyDown"" with switch over args.Key",High,https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-accelerators
57,Styling,Organize resources with merged dictionaries,Share styles and brushes via App.xaml MergedDictionaries instead of duplicating per page,MergedDictionaries in App.xaml for shared styles brushes and colors,Duplicating SolidColorBrush definitions on every page,"<Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source=""Themes/Brushes.xaml""/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources>",SolidColorBrush Color hardcoded inline on every page,Medium,https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-resource-dictionary
58,Architecture,Use AsyncRelayCommand for async commands,AsyncRelayCommand exposes IsRunning and supports cancellation for IO bound work,[RelayCommand] on async Task method or AsyncRelayCommand for IO work,async void event handlers or fire-and-forget Task.Run from button click,"[RelayCommand] private async Task LoadAsync(CancellationToken ct) { Items = await _service.FetchAsync(ct); }","private async void Button_Click(object s, RoutedEventArgs e) { await LoadAsync(); }",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/asyncrelaycommand
59,Architecture,Use ILogger for structured logging,Microsoft.Extensions.Logging ILogger<T> with DI for structured leveled logs,ILogger<T> injected via constructor for diagnostic logging,Debug.WriteLine or Console.WriteLine for app diagnostics,"public MainViewModel(ILogger<MainViewModel> logger) { _logger = logger; } _logger.LogInformation(""Loaded {Count} items"", count);","Debug.WriteLine($""Loaded {count} items"");",Medium,https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/overview
1 No Category Guideline Description Do Don't Code Good Code Bad Severity Docs URL
2 1 XAML Use x:Bind for compiled bindings Compile-time checked bindings with better performance x:Bind for type-safe bindings {Binding} when x:Bind works <TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}"/> <TextBlock Text="{Binding Title}"/> High https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
3 2 XAML Use x:Load for deferred loading Only instantiate UI elements when needed x:Load=False for hidden panels and dialogs Loading all UI upfront <StackPanel x:Load="{x:Bind ShowDetails, Mode=OneWay}"> Always-loaded collapsed panels Medium https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-load-attribute
4 3 XAML Use x:Phase for incremental rendering Load list items in phases for smooth scrolling x:Phase on secondary content in DataTemplates Loading all template content in phase 0 <TextBlock x:Phase="1" Text="{x:Bind Description}"/> All content in single phase for complex templates Medium https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
5 4 XAML Use x:DefaultBindMode Set default binding mode for a scope x:DefaultBindMode=OneWay on containers with many bindings Mode=OneWay on every individual x:Bind <StackPanel x:DefaultBindMode="OneWay"> Mode=OneWay repeated on 20 bindings Low https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
6 5 Controls Use NavigationView for app navigation WinUI 3 NavigationView with Left Top and LeftCompact display modes plus footer items NavigationView with PaneDisplayMode for main app shell Custom hamburger menu implementation <NavigationView><NavigationView.MenuItems><NavigationViewItem Content="Home"/></NavigationView.MenuItems></NavigationView> Custom SplitView with manual hamburger button High https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/navigationview
7 6 Controls Use InfoBar for status messages Non-intrusive informational messages InfoBar for success warning and error messages Custom styled StackPanel for status <InfoBar IsOpen="True" Severity="Warning" Title="Update available"/> <StackPanel Background="Yellow"><TextBlock Text="Warning"/></StackPanel> Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/infobar
8 7 Controls Use TeachingTip for onboarding Contextual tips attached to UI elements TeachingTip for feature discovery Custom popup for teaching <TeachingTip Target="{x:Bind SearchBox}" Title="Try searching"/> Custom Popup positioned near target element Low https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/teaching-tip
9 8 Controls Use ContentDialog for modal interactions Standard modal dialog pattern ContentDialog for confirmations and input Custom overlay Panel as dialog <ContentDialog Title="Delete?" PrimaryButtonText="Delete" CloseButtonText="Cancel"/> Grid overlay with manual focus trapping Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/dialogs
10 9 Controls Use BreadcrumbBar for hierarchy Show navigation path in hierarchical apps BreadcrumbBar for folder or category navigation Manual TextBlock breadcrumb chain <BreadcrumbBar ItemsSource="{x:Bind Breadcrumbs}"/> <StackPanel Orientation="Horizontal"><TextBlock Text="Home > Settings > Display"/></StackPanel> Low https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/breadcrumbbar
11 10 Styling Use Lightweight Styling Override control sub-properties via resources Lightweight styling resource keys to tweak controls Full ControlTemplate override for small changes <Button><Button.Resources><ResourceDictionary><ResourceDictionary.ThemeDictionaries><ResourceDictionary x:Key="Light"><SolidColorBrush x:Key="ButtonBackground" Color="MediumSlateBlue"/></ResourceDictionary></ResourceDictionary.ThemeDictionaries></ResourceDictionary></Button.Resources></Button> Full ControlTemplate copy to change background color High https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-styles#lightweight-styling
12 11 Styling Use WinUI theme resources Consistent Fluent Design colors and brushes WinUI theme resource keys for colors Hardcoded hex color values Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" Background="#FF2D2D30" High https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/color
13 12 Styling Support light and dark themes Respect user and system theme preference ThemeResource for theme-adaptive values Hardcoded colors that break in dark mode Foreground="{ThemeResource TextFillColorPrimaryBrush}" Foreground="Black" High https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-theme-resources
14 13 Styling Use Fluent Design system Acrylic Mica Reveal and rounded corners Built-in Fluent materials and effects Custom blur and shadow implementations <Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"/> Custom CompositionBrush recreating acrylic Medium https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials
15 14 Navigation Use Frame for page navigation Microsoft.UI.Xaml.Controls.Frame for WinUI 3 page navigation Frame.Navigate with page types and parameters Swapping UserControls in a ContentControl rootFrame.Navigate(typeof(SettingsPage), parameter); contentArea.Content = new SettingsControl(); Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages
16 15 Navigation Pass typed navigation parameters Type-safe data passing between pages Typed parameter in OnNavigatedTo Dictionary or string parsing for parameters protected override void OnNavigatedTo(NavigationEventArgs e) { var item = (Item)e.Parameter; } var id = int.Parse(e.Parameter.ToString()); Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages
17 16 Navigation Handle back navigation WinUI 3 uses NavigationView.BackRequested instead of UWP SystemNavigationManager Register NavigationView.BackRequested handler and manage back stack Ignoring back navigation navigationView.BackRequested += OnBackRequested; No back button support Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigation-history-and-backwards-navigation
18 17 Navigation Use deep linking Handle protocol activation so URIs route to the right page Register protocol then check ExtendedActivationKind.Protocol on activation Single entry point ignoring activation context AppInstance.GetCurrent().GetActivatedEventArgs() with ExtendedActivationKind.Protocol Ignoring activation arguments Medium https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation
19 18 Data Binding Use ObservableCollection for lists Notifies UI of collection changes ObservableCollection<T> for bound ItemsSources List<T> for bound collections ObservableCollection<Item> Items { get; } = new(); List<Item> Items { get; set; } = new(); High https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
20 19 Data Binding Use INotifyPropertyChanged Enable property change notification for UI updates INotifyPropertyChanged on ViewModels Properties without notification public string Name { get => _name; set => SetProperty(ref _name, value); } public string Name { get; set; } without notification High https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
21 20 Data Binding Use function binding with x:Bind Call methods directly in bindings x:Bind with method references for transforms IValueConverter for simple logic <TextBlock Visibility="{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}"/> IValueConverter class for bool to visibility Medium https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/function-bindings
22 21 Data Binding Specify Mode explicitly on x:Bind x:Bind defaults to OneTime not OneWay Mode=OneWay or Mode=TwoWay when updates needed Forgetting Mode and getting stale UI Text="{x:Bind Title, Mode=OneWay}" Text="{x:Bind Title}" expecting live updates High https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
23 22 Performance Use ItemsRepeater for custom lists Virtualizing layout with full control ItemsRepeater for custom list layouts ListView for highly customized item layouts <ItemsRepeater ItemsSource="{x:Bind Items}"><ItemsRepeater.Layout><StackLayout/></ItemsRepeater.Layout></ItemsRepeater> ListView with heavily modified template and removed chrome Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/items-repeater
24 23 Performance Use incremental loading Load data on demand as user scrolls ISupportIncrementalLoading for large data sets Loading entire dataset upfront class IncrementalItemSource : ObservableCollection<Item>, ISupportIncrementalLoading await LoadAllItems() on page load for 10K items Medium https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
25 24 Performance Reduce visual tree complexity Simpler trees render faster Minimal nesting in DataTemplates Deeply nested panels in item templates <StackPanel><TextBlock/><TextBlock/></StackPanel> <Grid><Border><StackPanel><Grid>... 8 levels deep Medium https://learn.microsoft.com/en-us/windows/apps/develop/performance/optimize-xaml-loading
26 25 Performance Use compiled bindings over reflection x:Bind generates code at compile time x:Bind for hot paths and list items {Binding} in DataTemplates and frequently updated UI <DataTemplate x:DataType="local:Item"><TextBlock Text="{x:Bind Name}"/></DataTemplate> <DataTemplate><TextBlock Text="{Binding Name}"/></DataTemplate> High https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
27 26 Threading Use DispatcherQueue not Dispatcher WinUI 3 uses Microsoft.UI.Dispatching.DispatcherQueue instead of UWP CoreDispatcher DispatcherQueue.TryEnqueue for UI thread access Dispatcher.RunAsync (UWP pattern) _dispatcherQueue.TryEnqueue(() => Status = "Done"); Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ...); High https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue
28 27 Threading Use async/await for IO operations Keep UI responsive during file and network access async/await for IO so the UI thread keeps rendering Synchronous IO on UI thread var data = await httpClient.GetStringAsync(url); var data = httpClient.GetStringAsync(url).Result; High https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
29 28 Threading Use Task.Run for CPU-bound work Offload compute to thread pool Task.Run for heavy computation Long-running CPU work on UI thread var result = await Task.Run(() => ProcessLargeDataSet()); var result = ProcessLargeDataSet(); blocking UI High https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming
30 29 Packaging Use WinAppSDK correctly Windows App SDK provides the runtime WinAppSDK NuGet package and WindowsAppSDK bootstrapper Mixing UWP and WinUI 3 APIs <PackageReference Include="Microsoft.WindowsAppSDK"/> Using Windows.UI.Xaml instead of Microsoft.UI.Xaml High https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/
31 30 Packaging Use unpackaged or packaged appropriately Choose deployment model for your scenario Packaged (MSIX) for Store distribution Unpackaged without considering API limitations <WindowsPackageType>None</WindowsPackageType> for unpackaged Assuming all APIs work in unpackaged mode Medium https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/deploy-packaged-apps
32 31 Packaging Use single-project MSIX Simplified packaging for single app Single-project MSIX packaging Separate WAP project when not needed <EnableMsixTooling>true</EnableMsixTooling> in csproj Separate Windows Application Packaging project for simple apps Low https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/single-project-msix
33 32 Accessibility Set AutomationProperties Enable Narrator and screen reader support AutomationProperties.Name on all interactive controls Controls without accessible names <Button AutomationProperties.Name="Save document"><FontIcon Glyph="&#xE74E;"/></Button> <Button><FontIcon Glyph="&#xE74E;"/></Button> without name High https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information
34 33 Accessibility Support keyboard navigation Full keyboard accessibility Tab navigation and access keys for all controls Mouse-only interactions <Button AccessKey="S" Content="Save"/> Interactive elements unreachable by keyboard High https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-interactions
35 34 Accessibility Use proper heading levels Screen readers use headings for navigation AutomationProperties.HeadingLevel on section headers All text at same heading level <TextBlock AutomationProperties.HeadingLevel="Level1" Text="Settings"/> <TextBlock Style="{StaticResource TitleTextBlockStyle}"/> without heading level Medium https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information
36 35 Accessibility Support high contrast Respect system high contrast settings ThemeResource brushes that adapt to high contrast Hardcoded colors ignoring high contrast Foreground="{ThemeResource TextFillColorPrimaryBrush}" Foreground="#333333" High https://learn.microsoft.com/en-us/windows/apps/design/accessibility/high-contrast-themes
37 36 Accessibility Test with Accessibility Insights Validate accessibility compliance Accessibility Insights for Windows scanning Manual accessibility checking only Run Accessibility Insights FastPass on every page Ship without accessibility testing Medium https://accessibilityinsights.io/
38 37 Architecture Use MVVM with CommunityToolkit Source generators reduce boilerplate [ObservableProperty] and [RelayCommand] attributes Manual INotifyPropertyChanged and ICommand [ObservableProperty] private string _title; [RelayCommand] private void Save() { } Full INotifyPropertyChanged implementation per property Medium https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
39 38 Architecture Use dependency injection Register services with Microsoft.Extensions.DI IServiceProvider for ViewModel and service resolution new ViewModel() and new Service() everywhere services.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>(); new MainViewModel(new DataService()) in code-behind Medium https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview
40 39 Architecture Use Template Studio patterns Start with proven architectural templates Template Studio for WinUI 3 project scaffolding Blank project with manual setup for complex apps WinUI 3 Template Studio with MVVM and navigation Blank App template for production app Low https://github.com/microsoft/TemplateStudio
41 40 Architecture Separate platform from business logic Keep business logic in .NET Standard or shared libraries Business logic in separate class library Business logic mixed with WinUI types Shared.Core project with no WinUI references ViewModel importing Microsoft.UI.Xaml types Medium https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
42 41 Architecture Use WinUI 3 Window management Proper window lifecycle management AppWindow API for multi-window scenarios Single Window assumption in complex apps var appWindow = this.AppWindow; Relying solely on MainWindow for everything Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows
43 42 Testing Unit test ViewModels Test logic independent of UI framework xUnit or MSTest on ViewModel properties and commands Testing through UI only [Fact] public async Task LoadItems_PopulatesCollection() { await vm.LoadCommand.ExecuteAsync(null); Assert.NotEmpty(vm.Items); } Manual testing by running the app Medium https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
44 43 Testing Use WinAppDriver for UI tests Automated UI testing for WinUI 3 WinAppDriver or Appium for end-to-end tests Manual regression testing var element = session.FindElementByAccessibilityId("SaveButton"); element.Click(); Click-through manual testing Medium https://github.com/microsoft/WinAppDriver
45 44 Testing Mock WinRT APIs in tests Isolate tests from platform dependencies Interface wrappers around WinRT APIs Direct WinRT API calls in testable code IFileService wrapping StorageFile APIs StorageFile.GetFileFromPathAsync directly in ViewModel Medium https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
46 45 Controls Use NumberBox for numeric input Built-in numeric entry with validation formatting and spin buttons NumberBox with Minimum Maximum and SpinButtonPlacementMode TextBox with manual numeric parsing and validation <NumberBox Value="{x:Bind Quantity, Mode=TwoWay}" Minimum="0" Maximum="100" SpinButtonPlacementMode="Inline"/> TextBox with regex validation for numbers Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/number-box
47 46 Controls Use Expander for collapsible sections Expandable content area with header for progressive disclosure Expander for settings groups and optional content Manual visibility toggling with buttons <Expander Header="Advanced Settings"><StackPanel><ToggleSwitch Header="Debug mode"/></StackPanel></Expander> Button toggling StackPanel.Visibility for collapsible content Low https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/expander
48 47 Controls Use ProgressRing and ProgressBar for loading Built-in loading indicators for determinate and indeterminate states ProgressRing for indeterminate and ProgressBar for determinate progress Custom spinning animation or text-based loading indicators <ProgressRing IsActive="{x:Bind IsLoading, Mode=OneWay}"/> <TextBlock Text="Loading..." Visibility="{x:Bind IsLoading}"/> Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/progress-controls
49 48 Layout Use VisualStateManager for responsive layouts Adapt UI layout to window size using adaptive triggers AdaptiveTrigger with MinWindowWidth for responsive breakpoints Fixed layouts that break at different window sizes <VisualState><VisualState.StateTriggers><AdaptiveTrigger MinWindowWidth="720"/></VisualState.StateTriggers><VisualState.Setters><Setter Target="sidebar.Visibility" Value="Visible"/></VisualState.Setters></VisualState> Fixed two-column layout at all window sizes High https://learn.microsoft.com/en-us/windows/apps/develop/ui/layouts-with-xaml
50 49 Lifecycle Handle app activation and launch WinUI 3 apps receive activation events for URI and notification launches Check LaunchActivatedEventArgs in OnLaunched for activation context Ignoring activation arguments losing deep link context protected override void OnLaunched(LaunchActivatedEventArgs args) { if (args.Arguments.Contains("settings")) NavigateToSettings(); } Empty OnLaunched ignoring all activation parameters Medium https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation
51 50 Lifecycle Use single instancing with AppInstance Prevent multiple app windows competing for resources AppInstance.FindOrRegisterForKey for single-instance enforcement Multiple instances with conflicting state var instance = AppInstance.FindOrRegisterForKey("main"); if (!instance.IsCurrent) { await instance.RedirectActivationToAsync(args); } No instance management allowing duplicate windows Medium https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-instancing
52 51 Lifecycle Save and restore app state Persist UI state across app restarts for continuity (ApplicationData APIs require packaged apps; unpackaged apps must use file IO or registry) Save state to local settings on window close or navigation Losing user context on every restart ApplicationData.Current.LocalSettings.Values["lastPage"] = currentPage; No state persistence losing navigation position on restart Medium https://learn.microsoft.com/en-us/windows/apps/develop/data/store-and-retrieve-app-data
53 52 Styling Choose Mica vs Acrylic by surface lifetime Mica is for long-lived primary surfaces like main windows; Acrylic is for transient light-dismiss surfaces like flyouts and context menus Mica on root window backgrounds and Acrylic on flyouts and overlays Acrylic on the main window or Mica on transient flyouts <Window.SystemBackdrop><MicaBackdrop/></Window.SystemBackdrop> ... <FlyoutPresenter Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"/> Acrylic on every Window background causing battery and perf cost Medium https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials
54 53 Styling Set SystemBackdrop on Window directly WinUI 3 1.3+ exposes Window.SystemBackdrop with MicaBackdrop and DesktopAcrylicBackdrop classes replacing manual MicaController plumbing Window.SystemBackdrop in XAML or code Hand-rolled MicaController wiring when SystemBackdrop API is available <Window.SystemBackdrop><MicaBackdrop Kind="Base"/></Window.SystemBackdrop> var ctrl = new Microsoft.UI.Composition.SystemBackdrops.MicaController(); ctrl.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>()); Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/system-backdrops
55 54 Architecture Open secondary windows with new Window WinUI 3 supports multiple top-level windows; each Window owns an AppWindow accessible via Window.AppWindow for size and position control new Window().Activate() for secondary windows tracking them in App state Faking multi-window via main-window content swaps or ContentDialog var settings = new SettingsWindow(); settings.Activate(); MainWindow.Content = new SettingsView(); when a separate window is needed Medium https://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows
56 55 Architecture Extend client area into the title bar Use Window.ExtendsContentIntoTitleBar with SetTitleBar to host custom XAML in the chrome while preserving caption buttons ExtendsContentIntoTitleBar=true plus SetTitleBar(element) for custom drag region Hardcoded chrome height or custom caption buttons that break with theme and size changes this.ExtendsContentIntoTitleBar = true; this.SetTitleBar(AppTitleBar); Padding="0,32,0,0" to reserve space without SetTitleBar leaves window non-draggable Medium https://learn.microsoft.com/en-us/windows/apps/develop/title-bar
57 56 Accessibility Use KeyboardAccelerator for shortcuts Map Ctrl/Alt/Shift combinations to commands using KeyboardAccelerator on UIElement KeyboardAccelerator with Modifiers and Key on relevant controls Manual KeyDown handlers swallowing shortcuts <Button Command="{x:Bind SaveCommand}"><Button.KeyboardAccelerators><KeyboardAccelerator Modifiers="Control" Key="S"/></Button.KeyboardAccelerators></Button> Window.PreviewKeyDown="OnKeyDown" with switch over args.Key High https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-accelerators
58 57 Styling Organize resources with merged dictionaries Share styles and brushes via App.xaml MergedDictionaries instead of duplicating per page MergedDictionaries in App.xaml for shared styles brushes and colors Duplicating SolidColorBrush definitions on every page <Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="Themes/Brushes.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources> SolidColorBrush Color hardcoded inline on every page Medium https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-resource-dictionary
59 58 Architecture Use AsyncRelayCommand for async commands AsyncRelayCommand exposes IsRunning and supports cancellation for IO bound work [RelayCommand] on async Task method or AsyncRelayCommand for IO work async void event handlers or fire-and-forget Task.Run from button click [RelayCommand] private async Task LoadAsync(CancellationToken ct) { Items = await _service.FetchAsync(ct); } private async void Button_Click(object s, RoutedEventArgs e) { await LoadAsync(); } Medium https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/asyncrelaycommand
60 59 Architecture Use ILogger for structured logging Microsoft.Extensions.Logging ILogger<T> with DI for structured leveled logs ILogger<T> injected via constructor for diagnostic logging Debug.WriteLine or Console.WriteLine for app diagnostics public MainViewModel(ILogger<MainViewModel> logger) { _logger = logger; } _logger.LogInformation("Loaded {Count} items", count); Debug.WriteLine($"Loaded {count} items"); Medium https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/overview

View File

@@ -0,0 +1,57 @@
No,Category,Guideline,Description,Do,Don't,Code Good,Code Bad,Severity,Docs URL
1,XAML,Use XAML for declarative UI,Define layout and visuals in XAML not code-behind,XAML for structure and styling,Build UI trees in C# code-behind,"<Button Content=""Save"" Click=""OnSave""/>",var btn = new Button(); btn.Content = "Save";,Low,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/
2,XAML,Set x:Class on root element,Connects XAML to its code-behind partial class,x:Class on Window UserControl and Page,"Missing x:Class or mismatched namespace","<Window x:Class=""MyApp.MainWindow"">","<Window> without x:Class",High,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/
3,XAML,Use x:Name sparingly,Only name elements accessed from code-behind,x:Name when code-behind reference is needed,Naming every element,"<TextBox x:Name=""SearchBox""/>","x:Name on every control",Low,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/
4,XAML,Prefer attached properties for layout,Grid.Row Grid.Column DockPanel.Dock etc,Attached properties for panel positioning,Margin hacks for alignment,"<Button Grid.Row=""1"" Grid.Column=""2""/>","<Button Margin=""200,100,0,0""/>",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/attached-properties-overview
5,XAML,Use routed events for tree-wide handling,Events bubble up or tunnel down the element tree letting parents handle child events with one handler,Handler at parent using TypeName.EventName syntax with e.Handled=true when consumed,Wiring identical handlers on every child when one parent handler suffices,"<StackPanel Button.Click=""OnAnyButtonClick"">","Click=""OnClick"" repeated on every Button under a common parent",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/events/routed-events-overview
6,Data Binding,Implement INotifyPropertyChanged,Enable UI updates when properties change,INotifyPropertyChanged on ViewModels,Public properties without notification,"public string Name { get => _name; set { if (_name != value) { _name = value; OnPropertyChanged(); } } }","public string Name { get; set; } without notification",High,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-property-change-notification
7,Data Binding,Use ObservableCollection for lists,Notifies UI of add remove and reset,ObservableCollection<T> for bound collections,List<T> or Array for bound ItemsSources,"ObservableCollection<Item> Items { get; } = new();","List<Item> Items { get; set; } = new();",High,https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1
8,Data Binding,Set DataContext at the right level,Enables binding for the visual subtree,DataContext on Window or root container,DataContext on every child control,"<Window DataContext=""{Binding Source={StaticResource VM}}"">","Setting DataContext on each TextBlock individually",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/
9,Data Binding,Prefer Binding over code-behind assignments,Declarative binding keeps UI and logic separate,"{Binding Path=Name} in XAML","textBlock.Text = viewModel.Name in code-behind","<TextBlock Text=""{Binding Name}""/>","Loaded event handler that sets every property",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-binding-overview
10,Data Binding,Use UpdateSourceTrigger appropriately,Controls when source updates,PropertyChanged for instant feedback,Default LostFocus when search-as-you-type is needed,"Text=""{Binding Query, UpdateSourceTrigger=PropertyChanged}""","Text=""{Binding Query}"" when search-as-you-type needed",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-control-when-the-textbox-text-updates-the-source
11,Data Binding,Use IValueConverter for display transforms,Convert data for presentation without changing the model,IValueConverter for bool-to-visibility etc,Visibility properties on ViewModel,"<TextBlock Visibility=""{Binding IsActive, Converter={StaticResource BoolToVis}}""/>","public Visibility IsActiveVisibility => IsActive ? Visibility.Visible : Visibility.Collapsed;",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-convert-bound-data
12,Data Binding,Use INotifyDataErrorInfo for validation,Surface validation errors to the binding system instead of ad-hoc error UI,ObservableValidator with DataAnnotations attributes,Throwing in setters or maintaining separate error properties,"public partial class FormVm : ObservableValidator { [ObservableProperty][NotifyDataErrorInfo][Required] private string _email; }","if (string.IsNullOrEmpty(Email)) ErrorMessage = ""Required"";",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/observablevalidator
13,Layout,Use Grid for complex layouts,Rows and columns with proportional or fixed sizing,Grid with RowDefinitions and ColumnDefinitions,Canvas with absolute positions for forms,"<Grid><Grid.RowDefinitions><RowDefinition Height=""Auto""/><RowDefinition Height=""*""/></Grid.RowDefinitions></Grid>","<Canvas><TextBox Canvas.Left=""50"" Canvas.Top=""80""/></Canvas>",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/grid
14,Layout,Use StackPanel for linear content,Simple vertical or horizontal stacking,StackPanel for toolbars and simple lists,Grid with single column for linear content,"<StackPanel Orientation=""Horizontal""><Button/><Button/></StackPanel>","<Grid><Grid.RowDefinitions>..12 Auto rows..</Grid.RowDefinitions></Grid>",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/stackpanel
15,Layout,Use DockPanel for docked regions,Dock children to edges with last child filling,DockPanel for shell layouts (menu top sidebar left),Nested StackPanels to simulate docking,"<DockPanel><Menu DockPanel.Dock=""Top""/><TreeView DockPanel.Dock=""Left""/><Frame/></DockPanel>","Nested StackPanels with fixed widths for shell",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/dockpanel
16,Layout,Avoid hardcoded sizes,Use Auto Star and MinWidth/MaxWidth,Proportional sizing with * and Auto,Fixed pixel widths on resizable content,"<ColumnDefinition Width=""2*""/>","<ColumnDefinition Width=""350""/> on main content",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/grid
17,Layout,Use ScrollViewer for overflow,Wrap content that may exceed available space,ScrollViewer around long forms or lists,Clipping content without scroll,"<ScrollViewer><StackPanel>...long content...</StackPanel></ScrollViewer>","<StackPanel> that clips off-screen items",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/scrollviewer
18,Styling,Use Resource Dictionaries,Centralize colors brushes and styles,ResourceDictionary in App.xaml for theme values,Inline colors and font sizes on every element,"<SolidColorBrush x:Key=""PrimaryBrush"" Color=""#0078D4""/>","<Button Background=""#0078D4""/> repeated everywhere",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/systems/xaml-resources-overview
19,Styling,Use pack URIs for embedded resources,Reference embedded images fonts and resource dictionaries via the pack scheme,"pack://application:,,, syntax for cross-assembly assets",File-system paths for resources compiled into the assembly,"<Image Source=""pack://application:,,,/MyApp;component/Resources/icon.png""/>","<Image Source=""C:\Resources\icon.png""/>",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/pack-uris-in-wpf
20,Styling,Use implicit styles,Apply a Style to all instances of a TargetType,Style with TargetType and no x:Key for defaults,Manually styling every Button instance,"<Style TargetType=""Button""><Setter Property=""Padding"" Value=""12,6""/></Style>","Padding=""12,6"" on every Button",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/styles-templates-overview
21,Styling,Use explicit styles with x:Key and BasedOn,Named variant styles that inherit from a base via BasedOn,x:Key styles that BasedOn an implicit or named style,Duplicating setters across variants,"<Style x:Key=""PrimaryButton"" TargetType=""Button"" BasedOn=""{StaticResource {x:Type Button}}"">","Copy-pasting 10 Setters into a second Style",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/styles-templates-overview
22,Styling,Prefer StaticResource over DynamicResource,StaticResource is resolved once and faster,StaticResource for values that do not change at runtime,DynamicResource for static theme values,"Background=""{StaticResource PrimaryBrush}""","Background=""{DynamicResource PrimaryBrush}"" when theme never changes",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/systems/xaml-resources-overview
23,Styling,Use ControlTemplate for full control,Override default rendering of a control,ControlTemplate when built-in styles are insufficient,Nesting extra panels to hide the default template,"<ControlTemplate TargetType=""Button""><Border><ContentPresenter/></Border></ControlTemplate>","Wrapping Button in Border to fake a custom look",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/how-to-create-apply-template
24,Styling,Use DataTemplate for data presentation,Define how data objects render in ItemsControls,DataTemplate for ListBox ComboBox and ItemsControl items,ToString overrides for display,"<DataTemplate DataType=""{x:Type local:Person}""><TextBlock Text=""{Binding FullName}""/></DataTemplate>","Relying on ToString() in ListBox",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-templating-overview
25,Styling,Use Fluent theme on .NET 9+,ThemeMode applies the Windows 11 Fluent style; values are Light Dark System and None (default Aero2),ThemeMode on Application or Window,Legacy Aero2 styling for new Windows 11 apps,"<Application ThemeMode=""System"">","<Application> with default Aero2 look",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/whats-new/net90
26,Commands,Use ICommand for user actions,Decouple UI actions from logic,ICommand implementations (RelayCommand DelegateCommand),Click event handlers in code-behind,"<Button Command=""{Binding SaveCommand}""/>","<Button Click=""OnSaveClick""/> with logic in code-behind",High,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview
27,Commands,Use CanExecute for enable/disable,Automatically disable controls when action unavailable; call NotifyCanExecuteChanged when state changes,CanExecute returning false to disable buttons,IsEnabled binding to a separate bool,"new RelayCommand(Save, () => !IsBusy)","<Button IsEnabled=""{Binding IsNotBusy}""/>",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview
28,Commands,Use RelayCommand or DelegateCommand,Avoid implementing ICommand from scratch every time,RelayCommand (CommunityToolkit.Mvvm) or DelegateCommand (Prism),New ICommand class per command,"[RelayCommand] private void Save() { }","class SaveCommand : ICommand { ... } for each action",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/relaycommand
29,Commands,Use AsyncRelayCommand for async operations,Tracks IsRunning and disables the command while it executes preventing re-entry,AsyncRelayCommand or [RelayCommand] on async Task method,async void event handlers in code-behind,[RelayCommand] private async Task SaveAsync() { await _service.SaveAsync(); },"private async void OnSaveClick(object s, RoutedEventArgs e) { await _service.SaveAsync(); }",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/asyncrelaycommand
30,Commands,Use CommandParameter for context,Pass data from the UI element to the command handler,CommandParameter for item-specific actions,Relying on SelectedItem in every command,"<Button Command=""{Binding DeleteCommand}"" CommandParameter=""{Binding}""/>","Command handler accessing SelectedItem directly",Low,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview
31,Commands,Use InputBindings for keyboard shortcuts,Bind keyboard gestures to commands without manual key handling,KeyBinding inside Window.InputBindings,Custom key handling in PreviewKeyDown,"<Window.InputBindings><KeyBinding Key=""S"" Modifiers=""Ctrl"" Command=""{Binding SaveCommand}""/></Window.InputBindings>",PreviewKeyDown handler checking for Ctrl+S,Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview
32,Performance,Use VirtualizingStackPanel for large lists,Only creates UI elements for visible items. ListBox/ListView virtualize by default; TreeView requires opt-in,VirtualizingStackPanel.IsVirtualizing=True (set on TreeView; default for ListBox),Disabling virtualization on long lists,"<TreeView VirtualizingStackPanel.IsVirtualizing=""True"" VirtualizingStackPanel.VirtualizationMode=""Recycling""/>","<ListBox ScrollViewer.CanContentScroll=""False""/>",High,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/optimizing-performance-controls
33,Performance,Freeze Freezable objects,Frozen brushes and geometries skip change tracking,Freeze brushes and pens that do not change,Mutable brushes used as static resources,"var brush = new SolidColorBrush(Colors.Blue); brush.Freeze();","new SolidColorBrush() without Freeze in resources",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/freezable-objects-overview
34,Performance,Use DependencyProperty for custom-control binding targets,Binding target properties on custom controls must be DependencyProperties (sources can be plain CLR properties with INPC),Define DependencyProperty for properties bound TO on a custom control,Plain CLR properties as binding targets on custom controls,"public static readonly DependencyProperty NameProperty = ...","public string Name { get; set; } as binding target on custom control",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-properties-overview
35,Performance,Use async for long operations,Keep UI thread responsive,async/await with Task.Run for CPU work,Synchronous operations that freeze the UI,"await Task.Run(() => HeavyComputation());","HeavyComputation() on UI thread",High,https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
36,Performance,Profile with PerfView and Visual Studio,Measure before optimizing,Visual Studio diagnostic tools and PerfView,Guessing at performance bottlenecks,Performance Profiler in Visual Studio (Alt+F2),Optimize without profiling,Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/optimizing-wpf-application-performance
37,Threading,Use Dispatcher for UI updates,UI elements can only be accessed from the UI thread,Dispatcher.Invoke or BeginInvoke from background threads,Accessing UI elements from background threads,"Application.Current.Dispatcher.Invoke(() => Status = ""Done"");","textBlock.Text = ""Done"" from Task.Run",High,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/threading-model
38,Threading,Prefer async/await over Dispatcher,Modern async code returns to UI context automatically,async/await which resumes on captured SynchronizationContext,Manual Dispatcher.BeginInvoke for every callback,"var data = await LoadDataAsync(); Items = data;","Dispatcher.BeginInvoke(() => Items = result) in callback",Medium,https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
39,Threading,Use Task.Run for CPU-bound work,Offload intensive work from UI thread,Task.Run for compute-bound work,Long-running computations on UI thread,"var result = await Task.Run(() => Compute());","var result = Compute(); on UI thread",High,https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming
40,Threading,Report progress from background tasks,Update UI with progress during long operations,IProgress<T> with Task.Run,Polling a shared variable for progress,"var progress = new Progress<int>(p => ProgressBar.Value = p); await Task.Run(() => Work(progress));","while (!done) { Thread.Sleep(100); check shared int; }",Medium,https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
41,Threading,Handle DispatcherUnhandledException,Catch unhandled UI-thread exceptions to log them and prevent the default WPF crash dialog,Subscribe in App.xaml or App.OnStartup and set e.Handled=true after logging,Letting WPF show its default crash dialog and silently shut down,"<Application DispatcherUnhandledException=""App_OnUnhandledException"">",No global handler so any unhandled exception crashes the app,High,https://learn.microsoft.com/en-us/dotnet/api/system.windows.application.dispatcherunhandledexception
42,Accessibility,Set AutomationProperties,Enable screen reader support,AutomationProperties.Name on interactive controls,Controls without automation names,"<Button AutomationProperties.Name=""Save document""/>","<Button><Image Source=""save.png""/></Button> without name",High,https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.automationproperties
43,Accessibility,Support keyboard navigation,All functionality reachable via keyboard,Tab order and KeyboardNavigation properties,Mouse-only interactions,"<StackPanel KeyboardNavigation.TabNavigation=""Cycle"">","Click handlers with no keyboard equivalent",High,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/focus-overview
44,Accessibility,Support high contrast themes,Respect Windows high contrast settings,SystemColors and SystemFonts resources,Hardcoded colors that disappear in high contrast,"Foreground=""{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}""","Foreground=""#333333"" everywhere",Medium,https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/accessibility-best-practices
45,Accessibility,Use appropriate control types,Semantic controls convey role to assistive tech,Button for actions CheckBox for toggles,Styled TextBlock with click handler as fake button,"<Button Content=""Submit""/>","<TextBlock MouseDown=""OnSubmitClick"" Text=""Submit""/>",High,https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/accessibility-best-practices
46,Accessibility,Support DPI scaling,Ensure UI is crisp at all display scale factors,Device-independent units and vector graphics,Pixel-based bitmaps that blur at high DPI,"<Path Data=""M 10,10 L 20,20""/> or DrawingImage","<Image Source=""icon_32x32.png""/> at 200% scaling",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/graphics
47,Accessibility,Declare PerMonitorV2 DPI awareness,WPF defaults to System-DPI-aware unless you opt into PerMonitorV2 via app.manifest,app.manifest with dpiAwareness PerMonitorV2,Default System DPI awareness for Windows 10/11 apps,"<dpiAwareness xmlns=""http://schemas.microsoft.com/SMI/2016/WindowsSettings"">PerMonitorV2</dpiAwareness>",No app.manifest leaving app at System DPI,Medium,https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
48,Architecture,Use MVVM pattern,Separate View ViewModel and Model concerns,MVVM with data binding and commands,Logic in code-behind,ViewModel with INotifyPropertyChanged and ICommand,MainWindow.xaml.cs with all business logic,High,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
49,Architecture,Override App.OnStartup for app initialization,Wire DI build the host resolve MainWindow and parse command-line args in OnStartup,Override OnStartup when DI or argument parsing is needed,Relying on StartupUri when MainWindow needs constructor injection,protected override void OnStartup(StartupEventArgs e) { _host.Start(); _host.Services.GetRequiredService<MainWindow>().Show(); },"<Application StartupUri=""MainWindow.xaml""/> when MainWindow has constructor dependencies",Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/application-management-overview
50,Architecture,Use dependency injection,Wire Microsoft.Extensions.Hosting Generic Host in App.OnStartup and resolve ViewModels from the container,Generic Host with Microsoft.Extensions.DependencyInjection,new Service() in ViewModel constructors,"services.AddTransient<MainViewModel>();","new MainViewModel(new DataService()) in App.xaml.cs",Medium,https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview
51,Architecture,Use CommunityToolkit.Mvvm,Source generators reduce MVVM boilerplate,[ObservableProperty] and [RelayCommand] attributes,Hand-written INotifyPropertyChanged for every property,"[ObservableProperty] private string _name;","private string _name; public string Name { get ... set ... OnPropertyChanged ... }",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
52,Architecture,Keep code-behind minimal,Code-behind should only contain view-specific logic,View logic like focus management and animations in code-behind,Business logic and data access in code-behind,Loaded handler that sets initial focus,Loaded handler that calls database and populates grid,Medium,https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/
53,Architecture,Use messaging for loose coupling,Communicate between ViewModels without references,WeakReferenceMessenger from CommunityToolkit.Mvvm,Direct ViewModel-to-ViewModel references,"WeakReferenceMessenger.Default.Send(new ItemSavedMessage(item));","MainViewModel.Instance.RefreshItems() from DetailViewModel",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/messenger
54,Testing,Unit test ViewModels,Test business logic independent of UI,xUnit or NUnit tests on ViewModel methods and properties,Manual testing through the UI only,"[Fact] public void Save_WhenValid_SetsIsBusy() { vm.Save(); Assert.True(vm.IsBusy); }","Clicking buttons in the running app to verify",Medium,https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
55,Testing,Mock services in tests,Isolate ViewModel from external dependencies,Moq or NSubstitute for service interfaces,Real database calls in unit tests,"var mock = new Mock<IDataService>(); var vm = new MainViewModel(mock.Object);","new MainViewModel(new SqlDataService()) in tests",Medium,https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
56,Testing,Use UI Automation for integration tests,Automated UI testing with Microsoft UI Automation,FlaUI or Appium 2 (appium-windows-driver) for end-to-end tests,Manual regression testing only,"AutomationElement.FindFirst(TreeScope.Children, condition)",Manual click-through testing,Medium,https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview
Can't render this file because it contains an unexpected character in line 2 and column 234.