Dart 3 and Flutter patterns across architecture, layout, routing, forms, networking, persistence, concurrency, animation, accessibility, localization, native interop, app size, and testing. Use when you say "build a Flutter screen", "why does my Column overflow", "set up GoRouter with an auth guard", "parse this JSON without jank", or "write a widget test for this form". Not for language-neutral SOLID, naming, and error-handling rules, use `coding-standards`.
Production patterns for Dart 3 and Flutter, from project layout down to the individual widget, with a reference
file per topic. The hub carries the rules that apply everywhere, and each reference carries the depth for one area.
Baseline
Assume Dart 3 and current stable Flutter. Verified here with flutter --version against Flutter 3.44.1 and Dart
3.12.1. Four defaults follow and change how code is written:
Material 3 is the default for ThemeData. Do not set useMaterial3, it is the only behaviour left.
Impeller is the default renderer on iOS and Android. Profile jank against Impeller, not the retired Skia path.
flutter build web --wasm compiles to WebAssembly, where dart:html, dart:js, and package:js do not work.
Use package:web and dart:js_interop.
The flutter_gen synthetic package is removed, so localizations import as
package:<your_app>/l10n/app_localizations.dart, never package:flutter_gen/....
Turn the strict analyzer modes on in analysis_options.yaml so the rules below are enforced by tooling rather
than by review. The exact block is in references/architecture.md.
When to activate
Writing or reviewing any Dart or Flutter code, including plain Dart packages with no widgets.
Structuring a new Flutter project, or refactoring one whose layers have blurred.
Debugging a layout overflow, a jank frame, a rebuild storm, or a setState() called after dispose() crash.
Wiring navigation, deep links, forms, HTTP clients, local databases, or platform channels.
Adding tests, localization, accessibility semantics, or a size budget to an existing app.
Setting up a Linux, macOS, or Windows machine to build Flutter.
When not to activate
Language-neutral design rules such as SOLID, DRY, naming, and error-handling shape. Use coding-standards.
Formatting and visual layout of source files in any language. Use code-formatter.
The red, green, refactor loop itself rather than Flutter test mechanics. Use tdd-workflow.
WCAG conformance for a web page or a non-Flutter front end. Use web-accessibility.
Reviewing a diff or a pull request for defects. Use code-reviewer.
Measuring and fixing backend or query latency behind the app. Use performance-optimization.
Never force-unwrap
! turns a compile-time question into a runtime crash. Dart already has an expression that carries the null case,
and late is the same failure mode one step removed. Reserve late for a field initialised in initState before
any read, such as an AnimationController, and use a nullable field everywhere else.
// BAD
final name = user!.name;
final ok = _formKey.currentState!.validate();
// GOOD
final name = user?.name ?? 'Unknown';
final ok = _formKey.currentState?.validate() ?? false;
Model state as a sealed type, never as nullable flags
A bag of isLoading, data, and error can represent loading with an error and no data, which is not a real
state. A sealed hierarchy makes that unrepresentable and makes switch exhaustive.
// BAD
class UserState { bool isLoading = false; User? user; String? error; }
// GOOD
sealed class UserState {}
final class UserLoading extends UserState { const UserLoading(); }
final class UserLoaded extends UserState { const UserLoaded(this.user); final User user; }
final class UserFailed extends UserState { const UserFailed(this.message); final String message; }
Widget build(BuildContext context) => switch (state) {
UserLoading() => const CircularProgressIndicator(),
UserLoaded(:final user) => UserCard(user: user),
UserFailed(:final message) => ErrorView(message: message),
};
Riverpod's AsyncValue<T> is this prebuilt. Generate data-class immutability with freezed, not by hand.
Guard BuildContext across every await
After an await the widget may be gone and the captured context points at a dead element. Without a mounted
field, capture what you need before the await and use the captured object afterwards.
// BAD
await authService.login(email, password);
context.go('/home');
// GOOD
await authService.login(email, password);
if (!mounted) return;
context.go('/home');
Extract widgets to classes, not to builder methods
A Widget _buildHeader() returns a subtree owned by the caller's element, so it rebuilds with the caller and can
never be const. A separate class gets its own element and stops a rebuild dead. Push const as far up the tree
as it goes, and keep the widget that watches changing state small.
// BAD
Widget _buildHeader() => Padding(padding: const EdgeInsets.all(16), child: Text(title));
// GOOD
class _PageHeader extends StatelessWidget {
const _PageHeader(this.title);
final String title;
@override
Widget build(BuildContext c) => Padding(padding: const EdgeInsets.all(16), child: Text(title));
}
Match the project's state management
Riverpod, BLoC, and ChangeNotifier all work. Mixing two in one feature is worse than any of them alone, so read
the existing code first and follow it.
Riverpod fits when dependencies form a graph and most state derives from other state. It is also the DI
container, so do not add get_it beside it.
BLoC fits event-driven features where the event log helps debugging and the team wants one rigid shape. Pair it
with get_it and injectable for DI.
ChangeNotifier with Provider fits small apps and a single screen's local state. It is a first-class Flutter
class, not a deprecated one. It mutates in place, so keep the notified value immutable and replace it wholesale.
Never run two of these in the same feature, and never expose a mutable collection from any of them. Worked
examples of all three sit in references/architecture.md.
Match the project's HTTP client
package:http and package:dio are both correct. http is smaller and is what the Flutter team documents. dio
adds interceptors, cancellation, and typed errors, which pay off once you need token refresh or retries. Check
pubspec.yaml first and use whichever is there.
// package:http
final response = await client.get(Uri.https('api.example.com', '/users/$id'));
if (response.statusCode != 200) throw HttpException('GET /users/$id failed');
final user = User.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
// package:dio
final response = await dio.get<Map<String, dynamic>>('/users/$id');
final user = User.fromJson(response.data!);
Both get the same treatment: build URLs with Uri.https, check the status code and throw rather than returning
null, and never leave a token in source.
Keep constraints bounded
Constraints go down, sizes go up, the parent sets position. A scrollable inside a flex box gets unbounded space on
the main axis and throws. Bound it rather than hardcoding a height.
// BAD
Column(children: [const Text('Header'), ListView(children: items)])
// GOOD
Column(children: [const Text('Header'), Expanded(child: ListView(children: items))])
Move CPU work off the main isolate
async and await do not create a thread, so an expensive synchronous parse still blocks the frame. I/O bound
work stays on the main isolate. Use Isolate.run only when a synchronous block measurably exceeds a frame budget,
and Isolate.spawn only when the work is long-lived and bidirectional.
// BAD
final photos = parsePhotos(response.body);
// GOOD
final photos = await Isolate.run(() => parsePhotos(response.body));
Route every user-facing string through localization
A literal in a widget, a validator, or a semantics label cannot be translated and cannot be tested per locale.
// BAD
return Semantics(label: 'Delete item', child: const Icon(Icons.delete));
// GOOD
return Semantics(label: AppLocalizations.of(context).deleteItem, child: const Icon(Icons.delete));
Doc comments
Default to none. A /// block is usually a sign that the code failed to explain itself. Extract the unclear block
into a well-named method or widget class, rename the parameters so they carry their own meaning, and tighten the
types. Do that first and most doc comments have nothing left to say. Code that explains itself cannot go stale, a
comment can. When one is genuinely needed, the prose is capped at five lines and is usually one. Every note about
a parameter, the return, or a thrown exception is capped at one line and only appears when it adds something. If
it does not fit on one line, shorten it or drop it. public_member_api_docs demanding a comment on every public
member is not a reason to write a sentence that adds nothing. Four rules decide what goes in.
Prose. One sentence saying what it does, then only what a caller cannot infer from the signature. Nothing more.
Describe a parameter, referenced as [holdFor], only when the name and the type do not already convey it,
meaning units, nullability, a valid range, or who owns it afterwards. [orderId] the order identifier is
noise, delete it.
Describe the return only when it is non-obvious.
Describe every exception a caller can act on, always. Dart has no checked exceptions and puts nothing about
throwing in the signature, so this one is genuinely contract rather than decoration.
Going past the five-line prose cap is allowed only when the contract cannot be stated in fewer lines, for example
a documented state machine, an ordering requirement, or a concurrency guarantee. It is an exception you justify in
review. The one-line cap on a note line has no exception: shorten it or delete it.
/// GOOD. One sentence, then only what the signature cannot say, one line per note.
/// Reserves stock for an order and holds it until the payment window closes.
///
/// [holdFor] is capped at 15 minutes.
/// Throws [InsufficientStockException] when the warehouse cannot cover it.
Future<Reservation> reserve(OrderId orderId, Duration holdFor);
/// BAD. Restates the signature and says nothing about the failure mode.
/// Reserves stock.
///
/// [orderId] the order identifier.
/// [holdFor] the hold duration.
/// Returns a reservation.
Future<Reservation> reserve(OrderId orderId, Duration holdFor);
Which reference to open for which task
Task
Reference
Layering a project, choosing a state manager, wiring DI, global error capture, flavors
references/architecture.md
Overflow errors, flex boxes, Stack, responsive and adaptive layouts
references/layout.md
GoRouter, auth redirects, nested navigation, deep links, passing and returning data
references/routing-and-navigation.md
Form and TextFormField, multi-field validation, submit gating, form tests
references/forms.md
REST calls with http or dio, interceptors, JSON code generation, background parsing
references/http-and-json.md
SQLite and drift, repositories, offline-first sync, image and scroll caching
Platform channels, Pigeon, FFI, AndroidView and UiKitView, Wasm and JS interop
references/native-interop-and-platform-views.md
Measuring bundle size, tree shaking, obfuscation, asset audits, runtime performance
references/app-size.md
Unit, widget, and integration tests, fakes, coverage, golden and plugin tests
references/testing.md
Installing the SDK and toolchain on Linux, macOS, or Windows, plus CI and packaging
references/environment-setup.md
Related skills
coding-standards for the cross-language engineering floor these patterns sit on.
code-formatter for source layout and reading flow in Dart and every other language here.
tdd-workflow for the red, green, refactor discipline behind references/testing.md.
web-accessibility for WCAG conformance when a Flutter web build sits inside a wider site.
code-reviewer for reviewing a Dart diff against all of the above.
build-dependency-management for pinning and admitting the packages named in these references.
Checklist
No ! force-unwrap and no late that is not initialised in initState.
Async state is a sealed type or AsyncValue, never a bag of nullable fields.
Every context use after an await is guarded by mounted or a pre-await capture.
Subtrees are widget classes, not _build...() methods, and const reaches as far up as it can.
One state-management library per feature, matching what the project already uses.
One HTTP client, matching pubspec.yaml, with URLs built through Uri.https.
No user-facing literal anywhere, including validators and semantics labels.
Every disposable (AnimationController, TextEditingController, StreamSubscription) is disposed.
Doc comments follow the four rules above, and none merely restate a signature.
flutter analyze is clean and flutter test --coverage passes at around 90% of real logic.
1---2name: dart-flutter-patterns3description: Dart 3 and Flutter patterns across architecture, layout, routing, forms, networking, persistence, concurrency, animation, accessibility, localization, native interop, app size, and testing. Use when you say "build a Flutter screen", "why does my Column overflow", "set up GoRouter with an auth guard", "parse this JSON without jank", or "write a widget test for this form". Not for language-neutral SOLID, naming, and error-handling rules, use `coding-standards`.4---56# Dart and Flutter Patterns78Production patterns for Dart 3 and Flutter, from project layout down to the individual widget, with a reference9file per topic. The hub carries the rules that apply everywhere, and each reference carries the depth for one area.1011---1213### Baseline1415Assume Dart 3 and current stable Flutter. Verified here with `flutter --version` against Flutter 3.44.1 and Dart163.12.1. Four defaults follow and change how code is written:1718- Material 3 is the default for `ThemeData`. Do not set `useMaterial3`, it is the only behaviour left.19- Impeller is the default renderer on iOS and Android. Profile jank against Impeller, not the retired Skia path.20- `flutter build web --wasm` compiles to WebAssembly, where `dart:html`, `dart:js`, and `package:js` do not work.21 Use `package:web` and `dart:js_interop`.22- The `flutter_gen` synthetic package is removed, so localizations import as23 `package:<your_app>/l10n/app_localizations.dart`, never `package:flutter_gen/...`.2425Turn the strict analyzer modes on in `analysis_options.yaml` so the rules below are enforced by tooling rather26than by review. The exact block is in [references/architecture.md](references/architecture.md).2728---2930### When to activate3132- Writing or reviewing any Dart or Flutter code, including plain Dart packages with no widgets.33- Structuring a new Flutter project, or refactoring one whose layers have blurred.34- Debugging a layout overflow, a jank frame, a rebuild storm, or a `setState() called after dispose()` crash.35- Wiring navigation, deep links, forms, HTTP clients, local databases, or platform channels.36- Adding tests, localization, accessibility semantics, or a size budget to an existing app.37- Setting up a Linux, macOS, or Windows machine to build Flutter.3839---4041### When not to activate4243- Language-neutral design rules such as SOLID, DRY, naming, and error-handling shape. Use `coding-standards`.44- Formatting and visual layout of source files in any language. Use `code-formatter`.45- The red, green, refactor loop itself rather than Flutter test mechanics. Use `tdd-workflow`.46- WCAG conformance for a web page or a non-Flutter front end. Use `web-accessibility`.47- Reviewing a diff or a pull request for defects. Use `code-reviewer`.48- Measuring and fixing backend or query latency behind the app. Use `performance-optimization`.4950---5152### Never force-unwrap5354`!` turns a compile-time question into a runtime crash. Dart already has an expression that carries the null case,55and `late` is the same failure mode one step removed. Reserve `late` for a field initialised in `initState` before56any read, such as an `AnimationController`, and use a nullable field everywhere else.5758```dart59// BAD60final name = user!.name;61final ok = _formKey.currentState!.validate();6263// GOOD64final name = user?.name ?? 'Unknown';65final ok = _formKey.currentState?.validate() ?? false;66```6768---6970### Model state as a sealed type, never as nullable flags7172A bag of `isLoading`, `data`, and `error` can represent loading with an error and no data, which is not a real73state. A sealed hierarchy makes that unrepresentable and makes `switch` exhaustive.7475```dart76// BAD77class UserState { bool isLoading = false; User? user; String? error; }7879// GOOD80sealed class UserState {}81final class UserLoading extends UserState { const UserLoading(); }82final class UserLoaded extends UserState { const UserLoaded(this.user); final User user; }83final class UserFailed extends UserState { const UserFailed(this.message); final String message; }8485Widget build(BuildContext context) => switch (state) {86 UserLoading() => const CircularProgressIndicator(),87 UserLoaded(:final user) => UserCard(user: user),88 UserFailed(:final message) => ErrorView(message: message),89};90```9192Riverpod's `AsyncValue<T>` is this prebuilt. Generate data-class immutability with `freezed`, not by hand.9394---9596### Guard BuildContext across every await9798After an `await` the widget may be gone and the captured `context` points at a dead element. Without a `mounted`99field, capture what you need before the await and use the captured object afterwards.100101```dart102// BAD103await authService.login(email, password);104context.go('/home');105106// GOOD107await authService.login(email, password);108if (!mounted) return;109context.go('/home');110```111112---113114### Extract widgets to classes, not to builder methods115116A `Widget _buildHeader()` returns a subtree owned by the caller's element, so it rebuilds with the caller and can117never be `const`. A separate class gets its own element and stops a rebuild dead. Push `const` as far up the tree118as it goes, and keep the widget that watches changing state small.119120```dart121// BAD122Widget _buildHeader() => Padding(padding: const EdgeInsets.all(16), child: Text(title));123124// GOOD125class _PageHeader extends StatelessWidget {126 const _PageHeader(this.title);127 final String title;128 @override129 Widget build(BuildContext c) => Padding(padding: const EdgeInsets.all(16), child: Text(title));130}131```132133---134135### Match the project's state management136137Riverpod, BLoC, and `ChangeNotifier` all work. Mixing two in one feature is worse than any of them alone, so read138the existing code first and follow it.139140- Riverpod fits when dependencies form a graph and most state derives from other state. It is also the DI141 container, so do not add `get_it` beside it.142- BLoC fits event-driven features where the event log helps debugging and the team wants one rigid shape. Pair it143 with `get_it` and `injectable` for DI.144- `ChangeNotifier` with `Provider` fits small apps and a single screen's local state. It is a first-class Flutter145 class, not a deprecated one. It mutates in place, so keep the notified value immutable and replace it wholesale.146147```dart148@riverpod149class CartNotifier extends _$CartNotifier {150 @override151 List<CartItem> build() => const [];152 void clear() => state = const [];153}154155class AuthCubit extends Cubit<AuthState> {156 AuthCubit(this._service) : super(const AuthLoading());157 final AuthService _service;158 Future<void> login(String email, String password) async =>159 emit(AuthLoaded(await _service.login(email, password)));160}161162class CartModel extends ChangeNotifier {163 List<CartItem> _items = const [];164 List<CartItem> get items => _items;165 void clear() { _items = const []; notifyListeners(); }166}167```168169Never run two of these in the same feature, and never expose a mutable collection from any of them. Worked170examples of all three sit in [references/architecture.md](references/architecture.md).171172---173174### Match the project's HTTP client175176`package:http` and `package:dio` are both correct. `http` is smaller and is what the Flutter team documents. `dio`177adds interceptors, cancellation, and typed errors, which pay off once you need token refresh or retries. Check178`pubspec.yaml` first and use whichever is there.179180```dart181// package:http182final response = await client.get(Uri.https('api.example.com', '/users/$id'));183if (response.statusCode != 200) throw HttpException('GET /users/$id failed');184final user = User.fromJson(jsonDecode(response.body) as Map<String, dynamic>);185186// package:dio187final response = await dio.get<Map<String, dynamic>>('/users/$id');188final user = User.fromJson(response.data!);189```190191Both get the same treatment: build URLs with `Uri.https`, check the status code and throw rather than returning192null, and never leave a token in source.193194---195196### Keep constraints bounded197198Constraints go down, sizes go up, the parent sets position. A scrollable inside a flex box gets unbounded space on199the main axis and throws. Bound it rather than hardcoding a height.200201```dart202// BAD203Column(children: [const Text('Header'), ListView(children: items)])204205// GOOD206Column(children: [const Text('Header'), Expanded(child: ListView(children: items))])207```208209---210211### Move CPU work off the main isolate212213`async` and `await` do not create a thread, so an expensive synchronous parse still blocks the frame. I/O bound214work stays on the main isolate. Use `Isolate.run` only when a synchronous block measurably exceeds a frame budget,215and `Isolate.spawn` only when the work is long-lived and bidirectional.216217```dart218// BAD219final photos = parsePhotos(response.body);220221// GOOD222final photos = await Isolate.run(() => parsePhotos(response.body));223```224225---226227### Route every user-facing string through localization228229A literal in a widget, a validator, or a semantics label cannot be translated and cannot be tested per locale.230231```dart232// BAD233return Semantics(label: 'Delete item', child: const Icon(Icons.delete));234235// GOOD236return Semantics(label: AppLocalizations.of(context).deleteItem, child: const Icon(Icons.delete));237```238239---240241### Doc comments242243Default to none. A `///` block is usually a sign that the code failed to explain itself. Extract the unclear block244into a well-named method or widget class, rename the parameters so they carry their own meaning, and tighten the245types. Do that first and most doc comments have nothing left to say. Code that explains itself cannot go stale, a246comment can. When one is genuinely needed, the prose is capped at five lines and is usually one. Every note about247a parameter, the return, or a thrown exception is capped at one line and only appears when it adds something. If248it does not fit on one line, shorten it or drop it. `public_member_api_docs` demanding a comment on every public249member is not a reason to write a sentence that adds nothing. Four rules decide what goes in.2502511. Prose. One sentence saying what it does, then only what a caller cannot infer from the signature. Nothing more.2522. Describe a parameter, referenced as `[holdFor]`, only when the name and the type do not already convey it,253 meaning units, nullability, a valid range, or who owns it afterwards. `[orderId] the order identifier` is254 noise, delete it.2553. Describe the return only when it is non-obvious.2564. Describe every exception a caller can act on, always. Dart has no checked exceptions and puts nothing about257 throwing in the signature, so this one is genuinely contract rather than decoration.258259Going past the five-line prose cap is allowed only when the contract cannot be stated in fewer lines, for example260a documented state machine, an ordering requirement, or a concurrency guarantee. It is an exception you justify in261review. The one-line cap on a note line has no exception: shorten it or delete it.262263```dart264/// GOOD. One sentence, then only what the signature cannot say, one line per note.265/// Reserves stock for an order and holds it until the payment window closes.266///267/// [holdFor] is capped at 15 minutes.268/// Throws [InsufficientStockException] when the warehouse cannot cover it.269Future<Reservation> reserve(OrderId orderId, Duration holdFor);270271/// BAD. Restates the signature and says nothing about the failure mode.272/// Reserves stock.273///274/// [orderId] the order identifier.275/// [holdFor] the hold duration.276/// Returns a reservation.277Future<Reservation> reserve(OrderId orderId, Duration holdFor);278```279280---281282### Which reference to open for which task283284| Task | Reference |285| --- | --- |286| Layering a project, choosing a state manager, wiring DI, global error capture, flavors | [references/architecture.md](references/architecture.md) |287| Overflow errors, flex boxes, `Stack`, responsive and adaptive layouts | [references/layout.md](references/layout.md) |288| GoRouter, auth redirects, nested navigation, deep links, passing and returning data | [references/routing-and-navigation.md](references/routing-and-navigation.md) |289| `Form` and `TextFormField`, multi-field validation, submit gating, form tests | [references/forms.md](references/forms.md) |290| REST calls with `http` or `dio`, interceptors, JSON code generation, background parsing | [references/http-and-json.md](references/http-and-json.md) |291| SQLite and `drift`, repositories, offline-first sync, image and scroll caching | [references/databases-and-caching.md](references/databases-and-caching.md) |292| `Future`, `Stream`, structured concurrency, `Isolate.run`, long-lived workers | [references/concurrency.md](references/concurrency.md) |293| Implicit and explicit animations, `Hero`, staggering, physics, custom route transitions | [references/animation.md](references/animation.md) |294| Semantics, screen readers, tap targets, contrast, font scaling, web semantics | [references/accessibility.md](references/accessibility.md) |295| ARB files, `gen-l10n`, plurals and selects, locale resolution, iOS bundle setup | [references/localization.md](references/localization.md) |296| Platform channels, Pigeon, FFI, `AndroidView` and `UiKitView`, Wasm and JS interop | [references/native-interop-and-platform-views.md](references/native-interop-and-platform-views.md) |297| Measuring bundle size, tree shaking, obfuscation, asset audits, runtime performance | [references/app-size.md](references/app-size.md) |298| Unit, widget, and integration tests, fakes, coverage, golden and plugin tests | [references/testing.md](references/testing.md) |299| Installing the SDK and toolchain on Linux, macOS, or Windows, plus CI and packaging | [references/environment-setup.md](references/environment-setup.md) |300301---302303### Related skills304305- `coding-standards` for the cross-language engineering floor these patterns sit on.306- `code-formatter` for source layout and reading flow in Dart and every other language here.307- `tdd-workflow` for the red, green, refactor discipline behind [references/testing.md](references/testing.md).308- `web-accessibility` for WCAG conformance when a Flutter web build sits inside a wider site.309- `code-reviewer` for reviewing a Dart diff against all of the above.310- `build-dependency-management` for pinning and admitting the packages named in these references.311312---313314### Checklist315316- [ ] No `!` force-unwrap and no `late` that is not initialised in `initState`.317- [ ] Async state is a sealed type or `AsyncValue`, never a bag of nullable fields.318- [ ] Every `context` use after an `await` is guarded by `mounted` or a pre-await capture.319- [ ] Subtrees are widget classes, not `_build...()` methods, and `const` reaches as far up as it can.320- [ ] One state-management library per feature, matching what the project already uses.321- [ ] One HTTP client, matching `pubspec.yaml`, with URLs built through `Uri.https`.322- [ ] No user-facing literal anywhere, including validators and semantics labels.323- [ ] Every disposable (`AnimationController`, `TextEditingController`, `StreamSubscription`) is disposed.324- [ ] Doc comments follow the four rules above, and none merely restate a signature.325- [ ] `flutter analyze` is clean and `flutter test --coverage` passes at around 90% of real logic.
Run npx skillmds@latest add lukk17/dart-flutter-patterns in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Dart 3 and Flutter patterns across architecture, layout, routing, forms, networking, persistence, concurrency, animation, accessibility, localization, native interop, app size, and testing. Use when you say "build a Flutter screen", "why does my Column overflow", "set up GoRouter with an auth guard", "parse this JSON without jank", or "write a widget test for this form". Not for language-neutral SOLID, naming, and error-handling rules, use `coding-standards`. It is listed under Web & Frontend on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
Lukk17 (@lukk17) published this skill. Their other Agent Skills are listed on their SkillMD profile.