diff --git a/docs/en/Power-Tools-Custom-Code-Angular.md b/docs/en/Power-Tools-Custom-Code-Angular.md
new file mode 100644
index 00000000..48e0c9d8
--- /dev/null
+++ b/docs/en/Power-Tools-Custom-Code-Angular.md
@@ -0,0 +1,247 @@
+# Power Tools Custom Code - Angular
+
+ASP.NET Zero Power Tools generates code that can be fully regenerated whenever you change an entity. The **Custom Code** feature lets you add your own code to the generated pages and classes so that your additions survive regeneration.
+
+This page covers how Custom Code works in **Angular** projects. For other UI frameworks, see [Custom Code - MVC](Power-Tools-Custom-Code-Mvc.md) and [Custom Code - React](Power-Tools-Custom-Code-React.md). For the MAUI mobile app, see [Custom Code - MAUI](Power-Tools-Custom-Code-Maui.md).
+
+## Enabling Custom Code
+
+Open Power Tools, select your entity, and turn on the **Generate Overridable Entity** switch in the Entity Information section.
+
+
+
+When this switch is on, Power Tools generates additional extension files that you own. These files are created once and never overwritten, even when you regenerate the entity.
+
+> The switch affects both the server side and the client side.
+
+### Requirements
+
+**Project version.** The Angular component split ships with the ng-zorro template set, which Power Tools uses
+for **v15.5+** projects. On v15.3-v15.4 (PrimeNG templates) and older (module-based templates) the switch
+still generates the server-side extension files, but no `.component.base.ts` split and no `az:begin` slots in
+the HTML.
+
+### Limitations
+
+**Master-detail child entities.** The switch has no effect on the server layer of a child entity in a
+master-detail pair - none of the server-side extension files are generated for it. On the client, a child
+entity gets the split for its detail/view component only; the list page and create/edit components are
+generated for the parent, not the child.
+
+## How It Works
+
+Angular uses two complementary mechanisms to preserve your code during regeneration:
+
+| Mechanism | How It Works | Applies To |
+|---|---|---|
+| **Extension files** (SkipIfExists) | A base class is regenerated every time. A developer-owned subclass is generated once and never overwritten. You override methods or add logic in the subclass. | Server-side C# files and Angular TypeScript components |
+| **Preserved region slots** (`az:begin` / `az:end`) | Named regions inside regenerated HTML templates. Power Tools preserves any markup you place between the markers. | Angular component HTML templates |
+
+## Server Side
+
+When **Generate Overridable Entity** is enabled, the following server-side extension files are generated. These are shared across all UI frameworks.
+
+Power Tools splits each generated class in two: the generated half keeps the original file name and gains a
+`Base` suffix on the **class** name, and your half is a new `.Extended.cs` file holding a subclass at the
+original class name. Existing references keep resolving to your class, so nothing else in the solution has to
+change.
+
+For a `Product` entity, the generated files are:
+
+| Your file (never overwritten) | Regenerated file | Purpose |
+|---|---|---|
+| `ProductsAppService.Extended.cs` | `ProductsAppService.cs` | Override application service methods |
+| `IProductsAppService.Extended.cs` | `IProductsAppService.cs` | Extend the service interface |
+| `Product.cs` | `ProductBase.cs` | Add custom properties or methods to the entity |
+| `CreateOrEditProductDto.Extended.cs` | `CreateOrEditProductDto.cs` | Extend the input DTO |
+| `ProductDto.Extended.cs` | `ProductDto.cs` | Extend the list DTO |
+| `GetAllProductsInput.Extended.cs` | `GetAllProductsInput.cs` | Add custom filter parameters |
+| `GetAllProductsForExcelInput.Extended.cs` | `GetAllProductsForExcelInput.cs` | Add custom Excel export filter parameters |
+| `GetProductForViewDto.Extended.cs` | `GetProductForViewDto.cs` | Extend the view/list output DTO |
+| `GetProductForEditOutput.Extended.cs` | `GetProductForEditOutput.cs` | Extend the edit output DTO |
+| `ProductXLookupTableDto.Extended.cs` | `ProductXLookupTableDto.cs` | Extend the lookup DTO for navigation property `X` |
+| `ProductsController.Extended.cs` | `ProductsController.cs` | Extend the `Web.Host` API controller |
+
+> **The entity is the one exception to the naming pattern.** Because EF Core maps the class by name, the
+> *file* is renamed rather than the class: `ProductBase.cs` is regenerated, and **`Product.cs` is yours** and
+> is never overwritten. Do not expect a `Product.Extended.cs`, and do not treat `Product.cs` as generated.
+
+Each `.Extended.cs` file contains:
+
+```csharp
+// Write your custom code here.
+// ASP.NET Zero Power Tools will not overwrite this class
+// when you regenerate the related entity.
+```
+
+The base files (without `.Extended`) are fully regenerated on every run. New entity properties automatically flow into them. Your customizations in the extension files are preserved because Power Tools never touches them after the first generation.
+
+## Angular TypeScript - Extension Files
+
+When you enable **Generate Overridable Entity**, each Angular component is split into two TypeScript files:
+
+| Generated File | Description |
+|---|---|
+| `*.component.base.ts` | Abstract base class with all generated logic. **Regenerated every time.** |
+| `*.component.ts` | Your subclass that extends the base. **Generated once, never overwritten.** |
+
+This pattern applies to all component types:
+
+- **Page component** -- the list page
+- **Create/edit component** -- modal, full page, or offcanvas variant
+- **Detail/view component** -- modal or full page variant
+
+### Example for a `Product` Entity
+
+```
+products.component.base.ts ← regenerated (abstract base)
+products.component.ts ← yours (extends ProductsComponentBase)
+
+create-or-edit-product-modal.component.base.ts ← regenerated
+create-or-edit-product-modal.component.ts ← yours
+
+view-product-modal.component.base.ts ← regenerated
+view-product-modal.component.ts ← yours
+```
+
+### Customizing in the Subclass
+
+Your `.component.ts` extends the base class. Override any method to customize behavior:
+
+```typescript
+import { ProductsComponentBase } from "./products.component.base";
+
+@Component({
+ // ...
+})
+export class ProductsComponent extends ProductsComponentBase {
+ // Write your custom code here.
+ // ASP.NET Zero Power Tools will not overwrite this class
+ // when you regenerate the related entity.
+}
+```
+
+Because the concrete class sits at the standard `.component.ts` path, routing, lazy-loading, and module imports remain untouched. When you regenerate, the base class picks up new entity properties and the subclass keeps your code.
+
+**Key points about the base class:**
+
+- The class is declared `abstract` so it cannot be instantiated directly.
+- Members use `protected` access (instead of `private`) so your subclass can reach them.
+- The standalone imports array is exported as a named constant (e.g. `ProductsComponentImports`) so the subclass can use it in its `@Component` decorator.
+- The `@Component` decorator on the base has `{ template: '' }` -- just a stub. The real template belongs to the concrete class.
+
+## Angular HTML - Preserved Region Slots
+
+Angular HTML templates contain named `az:begin` / `az:end` markers. You can place custom markup between these markers and it will be preserved on regeneration. The rest of the template is fully regenerated.
+
+### List Page Slots
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Create/Edit Page Slots
+
+```html
+
+
+
+```
+
+### Detail/View Page Slots
+
+```html
+
+
+
+```
+
+### Slot Reference
+
+| Slot Name | File | Purpose |
+|---|---|---|
+| `filters` | List page HTML | Custom advanced filter controls |
+| `column-headers` | List page HTML | Custom table column headers (`
`) |
+| `column-cells` | List page HTML | Custom table column cells (`
`) |
+| `row-actions` | List page HTML | Custom row action menu items |
+| `toolbar-actions` | List page HTML | Custom toolbar buttons |
+| `form-fields` | Create/edit HTML | Custom form fields |
+| `detail-fields` | Detail/view HTML | Custom detail display fields |
+
+> The `column-headers` and `column-cells` slots are paired. Adding a custom column requires entries in both slots.
+
+### Example - Adding a Custom Filter
+
+```html
+
+
+
+
+
+
+```
+
+After regeneration, your `
` stays in place while the rest of the template is updated with any new entity properties.
+
+### Example - Adding a Custom Table Column
+
+```html
+
+
Custom Score
+
+
+
+
{{ record.product.customScore }}
+
+```
+
+## Preserved Region Rules
+
+The `az:begin` / `az:end` markers follow these rules:
+
+| Rule | Description |
+|---|---|
+| **Comment wrapper** | Must use HTML comment syntax: `` |
+| **Region names** | Letters, digits, underscores, hyphens, and dots only |
+| **No nesting** | Regions cannot be placed inside other regions |
+| **No duplicates** | Each region name must be unique within a file |
+| **Empty regions are no-ops** | If you don't add anything between the markers, the region is ignored |
+
+## Safety Features
+
+Power Tools includes safety mechanisms to prevent code loss:
+
+- **Abort on malformed markers:** If region markers are broken (unclosed, nested, duplicated, mismatched), the entire file is left untouched. No partial merge ever occurs.
+- **Orphan rescue:** If a region name existed in the old file but not in the newly generated output (for example, if a template update removes a slot), the content is saved to a sidecar file (`.orphaned.txt`) and a warning is shown. Content is never silently dropped.
+- **Ownership guard:** Toggling the **Generate Overridable Entity** switch on an entity that already has
+ generated files is handled in both directions:
+ - Turning it **off** points a fully generated template back at the file that holds your code. Power Tools
+ detects your code and refuses to overwrite it.
+ - Turning it **on** makes a path that used to be fully generated developer-owned. The file already sitting
+ there is old generated code, so Power Tools **deletes it** and lets the template write the new split -
+ saving a copy as `.bak` beside it first. If you had hand-edited that file, your changes are in
+ the `.bak`, not in the regenerated one.
+
+## Next
+
+[How to Create & Edit Power Tools Templates](How-To-Create-Edit-Power-Tools-Templates.md)
diff --git a/docs/en/Power-Tools-Custom-Code-Maui.md b/docs/en/Power-Tools-Custom-Code-Maui.md
new file mode 100644
index 00000000..755a7cda
--- /dev/null
+++ b/docs/en/Power-Tools-Custom-Code-Maui.md
@@ -0,0 +1,159 @@
+# Power Tools Custom Code - MAUI
+
+ASP.NET Zero Power Tools generates code that can be fully regenerated whenever you change an entity. The **Custom Code** feature lets you add your own code to the generated pages and classes so that your additions survive regeneration.
+
+This page covers how Custom Code works in the **MAUI Blazor Hybrid** mobile app. The MAUI app ships alongside every UI framework, so this page applies whether your web project is [Angular](Power-Tools-Custom-Code-Angular.md), [ASP.NET Core MVC](Power-Tools-Custom-Code-Mvc.md), or [React](Power-Tools-Custom-Code-React.md).
+
+## Enabling Custom Code
+
+MAUI Custom Code has two layers, each with its own requirements:
+
+| Layer | What it gives you | What to enable |
+|---|---|---|
+| **Preserved region slots** (`az:begin` / `az:end`) | Named markers inside `.razor` pages where you can place custom markup that survives regeneration. | **Mobile** switch only |
+| **Partial class files** (`.custom.cs`) | A second `partial` of the same component class where you can add fields, methods, or overrides. | **Mobile** + **Generate Overridable Entity** switches, on a **v15.5+** project |
+
+### Requirements
+
+**Region slots** are available on any project that supports MAUI code generation (**v13.3+**). Turn on the **Mobile** switch and the generated `.razor` pages will include `az:begin` / `az:end` markers.
+
+**Partial class files** require all three conditions:
+
+1. The **Mobile** switch is on.
+2. The **Generate Overridable Entity** switch is on.
+3. The project version is **v15.5** or later.
+
+On a v13.3–v15.4 project the **Mobile** switch still produces pages with region slots, but the `.custom.cs` companion files are not generated.
+
+**Generate Overridable Entity** alone (without **Mobile**) customizes the server side and the web client but leaves the MAUI app untouched.
+
+**Detail page.** `ViewProduct.custom.cs` is generated only when the entity's MAUI view page is enabled — the **Create View** option that appears under **Mobile** in the Visual Studio extension.
+
+## How It Works
+
+MAUI uses a third approach, different from Angular's base/subclass split and React's hook files:
+
+| Mechanism | How It Works | Applies To |
+|---|---|---|
+| **Partial class files** (SkipIfExists) | A second `partial` of the same component class. Generated once, never overwritten. No separate base class to register and no wiring. Requires **Generate Overridable Entity** + **v15.5+**. | MAUI Razor component code (`.custom.cs`) |
+| **Preserved region slots** (`az:begin` / `az:end`) | Named regions inside regenerated `.razor` markup. Power Tools preserves any markup you place between the markers. Available with **Mobile** switch only (v13.3+). | MAUI Razor pages (`.razor`) |
+
+Because your code is a partial of the *same* class rather than a subclass, there is no separate type to register and no route to update - the component is still `ProductIndex`. The generated `.razor.cs` beside it keeps being rewritten on every run, so new entity properties keep flowing in.
+
+## Partial Class Files
+
+For a `Product` entity with namespace `Inventory`, Power Tools generates these files under `.Maui\Pages\Inventory\`:
+
+| Your file (never overwritten) | Regenerated file | Purpose |
+|---|---|---|
+| `ProductIndex.custom.cs` | `ProductIndex.razor.cs` | Add fields, methods, or overrides for the list page |
+| `CreateOrEditProduct.custom.cs` | `CreateOrEditProduct.razor.cs` | Add fields, methods, or overrides for the create/edit page |
+| `ViewProduct.custom.cs` | `ViewProduct.razor.cs` | Add fields, methods, or overrides for the detail page |
+
+Each `.custom.cs` file starts out as an empty partial:
+
+```csharp
+namespace MyCompany.MyProject.Maui.Pages.Inventory;
+
+public partial class ProductIndex
+{
+}
+```
+
+Add fields, methods, or lifecycle overrides (such as `OnInitializedAsync` from Blazor's `ComponentBase`) here. Because it is the same class, you can reach any `protected` member the generated `.razor.cs` declares without any extra plumbing.
+
+### Example - Running Custom Logic After the Page Loads
+
+```csharp
+namespace MyCompany.MyProject.Maui.Pages.Inventory;
+
+public partial class ProductIndex
+{
+ private int _lowStockCount;
+
+ protected override async Task OnInitializedAsync()
+ {
+ await base.OnInitializedAsync();
+ _lowStockCount = await CountLowStockAsync();
+ }
+
+ private Task CountLowStockAsync()
+ {
+ // your logic here
+ return Task.FromResult(0);
+ }
+}
+```
+
+## Razor - Preserved Region Slots
+
+MAUI `.razor` pages contain named `az:begin` / `az:end` markers. You can place custom markup between these markers and it will be preserved on regeneration. The rest of the page is fully regenerated.
+
+### Slot Reference
+
+| Slot Name | File | Purpose |
+|---|---|---|
+| `list-fields` | `ProductIndex.razor` | Custom fields in each list row |
+| `form-fields` | `CreateOrEditProduct.razor` | Custom form fields |
+| `detail-fields` | `ViewProduct.razor` | Custom detail display fields |
+
+### Example - Adding a Custom List Field
+
+```html
+
+
+
+```
+
+### Example - Adding a Custom Form Field
+
+```html
+
+
+
+
+
+
+```
+
+After regeneration, your markup stays in place while the rest of the page is updated with any new entity properties.
+
+## Preserved Region Rules
+
+The `az:begin` / `az:end` markers follow these rules:
+
+| Rule | Description |
+|---|---|
+| **Comment wrapper** | Must use HTML comment syntax in `.razor` files: `` |
+| **Region names** | Letters, digits, underscores, hyphens, and dots only |
+| **No nesting** | Regions cannot be placed inside other regions |
+| **No duplicates** | Each region name must be unique within a file |
+| **Empty regions are no-ops** | If you don't add anything between the markers, the region is ignored |
+
+## Server Side
+
+The MAUI app consumes the same application services as the web client, so the server-side extension files are the ones described on the [Angular](Power-Tools-Custom-Code-Angular.md#server-side), [MVC](Power-Tools-Custom-Code-Mvc.md#server-side), and [React](Power-Tools-Custom-Code-React.md#server-side) pages. They are generated once for the entity regardless of which clients you target.
+
+## Safety Features
+
+Power Tools includes safety mechanisms to prevent code loss:
+
+- **Abort on malformed markers:** If region markers are broken (unclosed, nested, duplicated, mismatched), the entire file is left untouched. No partial merge ever occurs.
+- **Orphan rescue:** If a region name existed in the old file but not in the newly generated output (for example, if a template update removes a slot), the content is saved to a sidecar file (`.orphaned.txt`) and a warning is shown. Content is never silently dropped.
+- **Ownership guard:** Toggling the **Generate Overridable Entity** switch on an entity that already has
+ generated `.custom.cs` files is handled in both directions:
+ - Turning it **off** points a fully generated template back at the file that holds your code. Power Tools
+ detects your code and refuses to overwrite it.
+ - Turning it **on** makes a path that used to be fully generated developer-owned. The file already sitting
+ there is old generated code, so Power Tools **deletes it** and lets the template write the new split -
+ saving a copy as `.bak` beside it first. If you had hand-edited that file, your changes are in
+ the `.bak`, not in the regenerated one.
+
+## Next
+
+[How to Create & Edit Power Tools Templates](How-To-Create-Edit-Power-Tools-Templates.md)
diff --git a/docs/en/Power-Tools-Custom-Code-Mvc.md b/docs/en/Power-Tools-Custom-Code-Mvc.md
new file mode 100644
index 00000000..224b0e81
--- /dev/null
+++ b/docs/en/Power-Tools-Custom-Code-Mvc.md
@@ -0,0 +1,246 @@
+# Power Tools Custom Code - MVC
+
+ASP.NET Zero Power Tools generates code that can be fully regenerated whenever you change an entity. The **Custom Code** feature lets you add your own code to the generated pages and classes so that your additions survive regeneration.
+
+This page covers how Custom Code works in **ASP.NET Core MVC** projects. For other UI frameworks, see [Custom Code - Angular](Power-Tools-Custom-Code-Angular.md) and [Custom Code - React](Power-Tools-Custom-Code-React.md). For the MAUI mobile app, see [Custom Code - MAUI](Power-Tools-Custom-Code-Maui.md).
+
+## Enabling Custom Code
+
+Open Power Tools, select your entity, and turn on the **Generate Overridable Entity** switch in the Entity Information section.
+
+
+
+When this switch is on, Power Tools generates additional extension files that you own. These files are created once and never overwritten, even when you regenerate the entity.
+
+> The switch affects both the server side and the client side.
+
+### Requirements
+
+**Project version.** The Controller and ViewModel extension files are available on all supported project
+versions. The `az:begin` / `az:end` region slots live in the current MVC template set, which Power Tools uses
+for **v15.3+** projects; older projects get the extension files but no region slots.
+
+### Limitations
+
+**Master-detail child entities.** The switch has no effect on the server layer of a child entity in a
+master-detail pair - none of the server-side extension files are generated for it, and neither is
+`Controller.Extended.cs`. The ViewModel extension files are still generated.
+
+## How It Works
+
+MVC uses two complementary mechanisms to preserve your code during regeneration:
+
+| Mechanism | How It Works | Applies To |
+|---|---|---|
+| **Extension files** (SkipIfExists) | A base class is regenerated every time. A developer-owned subclass is generated once and never overwritten. | Server-side C# files and MVC Controller / ViewModel classes |
+| **Preserved region slots** (`az:begin` / `az:end`) | Named regions inside regenerated files. Power Tools preserves any code you place between the markers. | Razor views (`.cshtml`) and JavaScript files (`.js`) |
+
+> Razor views and JavaScript files cannot use the base/subclass pattern, so they rely entirely on preserved region slots.
+
+## Server Side
+
+When **Generate Overridable Entity** is enabled, the following server-side extension files are generated. These are shared across all UI frameworks.
+
+Power Tools splits each generated class in two: the generated half keeps the original file name and gains a
+`Base` suffix on the **class** name, and your half is a new `.Extended.cs` file holding a subclass at the
+original class name. Existing references keep resolving to your class, so nothing else in the solution has to
+change.
+
+For a `Product` entity, the generated files are:
+
+| Your file (never overwritten) | Regenerated file | Purpose |
+|---|---|---|
+| `ProductsAppService.Extended.cs` | `ProductsAppService.cs` | Override application service methods |
+| `IProductsAppService.Extended.cs` | `IProductsAppService.cs` | Extend the service interface |
+| `Product.cs` | `ProductBase.cs` | Add custom properties or methods to the entity |
+| `CreateOrEditProductDto.Extended.cs` | `CreateOrEditProductDto.cs` | Extend the input DTO |
+| `ProductDto.Extended.cs` | `ProductDto.cs` | Extend the list DTO |
+| `GetAllProductsInput.Extended.cs` | `GetAllProductsInput.cs` | Add custom filter parameters |
+| `GetAllProductsForExcelInput.Extended.cs` | `GetAllProductsForExcelInput.cs` | Add custom Excel export filter parameters |
+| `GetProductForViewDto.Extended.cs` | `GetProductForViewDto.cs` | Extend the view/list output DTO |
+| `GetProductForEditOutput.Extended.cs` | `GetProductForEditOutput.cs` | Extend the edit output DTO |
+| `ProductXLookupTableDto.Extended.cs` | `ProductXLookupTableDto.cs` | Extend the lookup DTO for navigation property `X` |
+| `ProductsController.Extended.cs` | `ProductsController.cs` | Extend the `Web.Host` API controller |
+
+> **The entity is the one exception to the naming pattern.** Because EF Core maps the class by name, the
+> *file* is renamed rather than the class: `ProductBase.cs` is regenerated, and **`Product.cs` is yours** and
+> is never overwritten. Do not expect a `Product.Extended.cs`, and do not treat `Product.cs` as generated.
+
+Each `.Extended.cs` file contains:
+
+```csharp
+// Write your custom code here.
+// ASP.NET Zero Power Tools will not overwrite this class
+// when you regenerate the related entity.
+```
+
+The base files (without `.Extended`) are fully regenerated on every run. New entity properties automatically flow into them. Your customizations in the extension files are preserved because Power Tools never touches them after the first generation.
+
+## MVC C# - Extension Files
+
+When you enable **Generate Overridable Entity**, the MVC Controller and ViewModel classes are split into base and extension files:
+
+| Generated File | Description |
+|---|---|
+| `*Controller.cs` (base) | Abstract base controller with all generated logic. **Regenerated every time.** |
+| `*Controller.Extended.cs` | Your controller subclass. **Generated once, never overwritten.** |
+| `*ViewModel.cs` (base) | Abstract base view model. **Regenerated every time.** |
+| `*ViewModel.Extended.cs` | Your view model subclass. **Generated once, never overwritten.** |
+
+If the entity has a view-only page enabled, a `*ViewEntityViewModel.Extended.cs` is also generated.
+
+### Example for a `Product` Entity
+
+```
+ProductsController.cs ← regenerated (abstract base)
+ProductsController.Extended.cs ← yours (extends base)
+
+ProductsViewModel.cs ← regenerated (abstract base)
+ProductsViewModel.Extended.cs ← yours (extends base)
+```
+
+Override any method in the extension file:
+
+```csharp
+public class ProductsController : ProductsControllerBase
+{
+ // Write your custom code here.
+ // ASP.NET Zero Power Tools will not overwrite this class
+ // when you regenerate the related entity.
+}
+```
+
+## MVC Razor & JavaScript - Preserved Region Slots
+
+Razor views and JavaScript files use named `az:begin` / `az:end` markers. You can place custom markup or code between these markers and it will be preserved on regeneration.
+
+### Index Page - Razor Slots
+
+```html
+
+
+
+
+
+
+
+```
+
+### Index Page Toolbar - Razor Slot
+
+```html
+
+
+
+```
+
+### Index Page - JavaScript Slot
+
+```javascript
+// az:begin(columns)
+// Add custom DataTables columnDefs entries here
+// az:end(columns)
+```
+
+### Create/Edit Modal - Razor Slot
+
+```html
+
+
+
+```
+
+### View Entity Modal - Razor Slot
+
+```html
+
+
+
+```
+
+### Slot Reference
+
+| Slot Name | File | Comment Style | Purpose |
+|---|---|---|---|
+| `filters` | Index Razor (`.cshtml`) | HTML | Custom advanced filter controls |
+| `column-headers` | Index Razor (`.cshtml`) | HTML | Custom table column headers |
+| `toolbar-actions` | Index Razor (`.cshtml`) | HTML | Custom toolbar buttons |
+| `columns` | Index JavaScript (`.js`) | JS | Custom DataTables column definitions |
+| `form-fields` | Create/edit Razor (`.cshtml`) | HTML | Custom form fields |
+| `detail-fields` | View entity Razor (`.cshtml`) | HTML | Custom detail display fields |
+
+> The `column-headers` slot in the Razor file and the `columns` slot in the JavaScript file are **paired**. Adding a custom column requires entries in both files.
+
+### Example - Adding a Custom Column
+
+In the Razor file (`.cshtml`):
+
+```html
+
+
Custom Score
+
+```
+
+In the JavaScript file (`.js`):
+
+```javascript
+// az:begin(columns)
+{
+ data: "product.customScore",
+ name: "customScore",
+ render: function(data) { return data || "-"; }
+},
+// az:end(columns)
+```
+
+### Example - Adding a Custom Form Field
+
+```html
+
+
+
+
+
+
+```
+
+### Example - Adding a Custom Toolbar Button
+
+```html
+
+
+
+```
+
+## Preserved Region Rules
+
+The `az:begin` / `az:end` markers follow these rules:
+
+| Rule | Description |
+|---|---|
+| **Comment wrapper matches the file type** | HTML: ``, JavaScript: `// az:begin(name)` |
+| **Region names** | Letters, digits, underscores, hyphens, and dots only |
+| **No nesting** | Regions cannot be placed inside other regions |
+| **No duplicates** | Each region name must be unique within a file |
+| **Empty regions are no-ops** | If you don't add anything between the markers, the region is ignored |
+
+## Safety Features
+
+Power Tools includes safety mechanisms to prevent code loss:
+
+- **Abort on malformed markers:** If region markers are broken (unclosed, nested, duplicated, mismatched), the entire file is left untouched. No partial merge ever occurs.
+- **Orphan rescue:** If a region name existed in the old file but not in the newly generated output (for example, if a template update removes a slot), the content is saved to a sidecar file (`.orphaned.txt`) and a warning is shown. Content is never silently dropped.
+- **Ownership guard:** Toggling the **Generate Overridable Entity** switch on an entity that already has
+ generated files is handled in both directions:
+ - Turning it **off** points a fully generated template back at the file that holds your code. Power Tools
+ detects your code and refuses to overwrite it.
+ - Turning it **on** makes a path that used to be fully generated developer-owned. The file already sitting
+ there is old generated code, so Power Tools **deletes it** and lets the template write the new split -
+ saving a copy as `.bak` beside it first. If you had hand-edited that file, your changes are in
+ the `.bak`, not in the regenerated one.
+
+## Next
+
+[How to Create & Edit Power Tools Templates](How-To-Create-Edit-Power-Tools-Templates.md)
diff --git a/docs/en/Power-Tools-Custom-Code-React.md b/docs/en/Power-Tools-Custom-Code-React.md
new file mode 100644
index 00000000..5dbafbbe
--- /dev/null
+++ b/docs/en/Power-Tools-Custom-Code-React.md
@@ -0,0 +1,322 @@
+# Power Tools Custom Code - React
+
+ASP.NET Zero Power Tools generates code that can be fully regenerated whenever you change an entity. The **Custom Code** feature lets you add your own code to the generated pages and classes so that your additions survive regeneration.
+
+This page covers how Custom Code works in **React** projects. For other UI frameworks, see [Custom Code - Angular](Power-Tools-Custom-Code-Angular.md) and [Custom Code - MVC](Power-Tools-Custom-Code-Mvc.md). For the MAUI mobile app, see [Custom Code - MAUI](Power-Tools-Custom-Code-Maui.md).
+
+## Enabling Custom Code
+
+Open Power Tools, select your entity, and turn on the **Generate Overridable Entity** switch in the Entity Information section.
+
+
+
+When this switch is on, Power Tools generates additional customization files that you own. These files are created once and never overwritten, even when you regenerate the entity.
+
+> The switch affects both the server side and the client side.
+
+### Requirements
+
+**Project version.** Every React customization file requires a **v15.5+** project. React code generation
+itself is supported from v15.0, so on a v15.0-v15.4 project the switch generates the server-side extension
+files and nothing on the client. If you turn the switch on and no `customizations.tsx` appears, check the
+project version first.
+
+### Limitations
+
+**Master-detail child entities.** The switch has no effect on a child entity in a master-detail pair -
+neither the server-side extension files nor any of the React customization files are generated for it.
+
+## How It Works
+
+React uses a different approach than Angular and MVC. Instead of inline region markers or base/subclass patterns for UI files, React generates **separate customization files** with typed hook functions that the generated page imports and calls at fixed extension points.
+
+| Mechanism | How It Works | Applies To |
+|---|---|---|
+| **Extension files** (SkipIfExists) | A base class is regenerated every time. A developer-owned subclass is generated once and never overwritten. | Server-side C# files only |
+| **Customization files** | Separate `.tsx` files with hook functions. Never overwritten. The generated page imports and calls them. | React pages, forms, and detail views |
+| **Regenerated type definitions** | A `customizations.types.ts` file that is regenerated to keep TypeScript types in sync with the entity. | React customization contexts |
+
+## Server Side
+
+When **Generate Overridable Entity** is enabled, the following server-side extension files are generated. These are shared across all UI frameworks.
+
+Power Tools splits each generated class in two: the generated half keeps the original file name and gains a
+`Base` suffix on the **class** name, and your half is a new `.Extended.cs` file holding a subclass at the
+original class name. Existing references keep resolving to your class, so nothing else in the solution has to
+change.
+
+For a `Product` entity, the generated files are:
+
+| Your file (never overwritten) | Regenerated file | Purpose |
+|---|---|---|
+| `ProductsAppService.Extended.cs` | `ProductsAppService.cs` | Override application service methods |
+| `IProductsAppService.Extended.cs` | `IProductsAppService.cs` | Extend the service interface |
+| `Product.cs` | `ProductBase.cs` | Add custom properties or methods to the entity |
+| `CreateOrEditProductDto.Extended.cs` | `CreateOrEditProductDto.cs` | Extend the input DTO |
+| `ProductDto.Extended.cs` | `ProductDto.cs` | Extend the list DTO |
+| `GetAllProductsInput.Extended.cs` | `GetAllProductsInput.cs` | Add custom filter parameters |
+| `GetAllProductsForExcelInput.Extended.cs` | `GetAllProductsForExcelInput.cs` | Add custom Excel export filter parameters |
+| `GetProductForViewDto.Extended.cs` | `GetProductForViewDto.cs` | Extend the view/list output DTO |
+| `GetProductForEditOutput.Extended.cs` | `GetProductForEditOutput.cs` | Extend the edit output DTO |
+| `ProductXLookupTableDto.Extended.cs` | `ProductXLookupTableDto.cs` | Extend the lookup DTO for navigation property `X` |
+| `ProductsController.Extended.cs` | `ProductsController.cs` | Extend the `Web.Host` API controller |
+
+> **The entity is the one exception to the naming pattern.** Because EF Core maps the class by name, the
+> *file* is renamed rather than the class: `ProductBase.cs` is regenerated, and **`Product.cs` is yours** and
+> is never overwritten. Do not expect a `Product.Extended.cs`, and do not treat `Product.cs` as generated.
+
+Each `.Extended.cs` file contains:
+
+```csharp
+// Write your custom code here.
+// ASP.NET Zero Power Tools will not overwrite this class
+// when you regenerate the related entity.
+```
+
+The base files (without `.Extended`) are fully regenerated on every run. New entity properties automatically flow into them. Your customizations in the extension files are preserved because Power Tools never touches them after the first generation.
+
+## React Customization Files
+
+When you enable **Generate Overridable Entity**, Power Tools generates the following files alongside the main page:
+
+| Generated File | Overwritten? | Purpose |
+|---|---|---|
+| `customizations.tsx` | **Never** | Hook functions for the list page: extra columns, row actions, toolbar actions |
+| `customizations.types.ts` | **Yes** (regenerated) | TypeScript types for the customization context |
+| `CreateOrEdit*Modal.custom.tsx` | **Never** | Hook functions for the form: extra fields, before/after save |
+| `View*Modal.custom.tsx` | **Never** | Hook functions for the detail view: extra detail items |
+
+> `customizations.types.ts` is the only customization-related file that gets regenerated. This is intentional, when the entity shape changes, the type definitions update and any stale code in your customization files produces a compile error rather than breaking silently.
+
+### File Layout Example for a `Product` Entity
+
+```
+pages/admin/inventory/products/
+├── index.tsx ← regenerated (main page)
+├── customizations.tsx ← yours (list page hooks)
+├── customizations.types.ts ← regenerated (types)
+├── components/
+│ ├── CreateOrEditProductModal.tsx ← regenerated
+│ ├── CreateOrEditProductModal.custom.tsx ← yours (form hooks)
+│ ├── ViewProductModal.tsx ← regenerated
+│ └── ViewProductModal.custom.tsx ← yours (detail hooks)
+```
+
+## List Page Hooks
+
+The `customizations.tsx` file provides three hook functions for the list page:
+
+```tsx
+import type { ReactNode } from "react";
+import type {
+ ProductColumn,
+ ProductRowAction,
+ ProductsPageContext,
+} from "./customizations.types";
+
+/** Columns appended after the generated ones. */
+export const extraColumns = (ctx: ProductsPageContext): ProductColumn[] => [];
+
+/** Extra entries for a row's action menu, appended after the generated ones. */
+export const extraRowActions = (
+ ctx: ProductsPageContext,
+ record: Parameters>[1],
+): ProductRowAction[] => [];
+
+/** Extra buttons in the page header, rendered after the generated ones. */
+export const extraToolbarActions = (ctx: ProductsPageContext): ReactNode => null;
+```
+
+The generated `index.tsx` imports these functions and calls them at the right points:
+
+- `extraColumns` is spread into the Ant Design table column definitions
+- `extraRowActions` is pushed into each row's action dropdown menu
+- `extraToolbarActions` is rendered in the page header area
+
+### Example - Adding a Custom Column
+
+```tsx
+export const extraColumns = (ctx: ProductsPageContext): ProductColumn[] => [
+ {
+ title: "Custom Score",
+ dataIndex: ["product", "customScore"],
+ render: (text: any) => {text ?? "-"},
+ },
+];
+```
+
+### Example - Adding a Custom Toolbar Button
+
+```tsx
+export const extraToolbarActions = (ctx: ProductsPageContext): ReactNode => (
+
+);
+```
+
+### Example - Adding a Custom Row Action
+
+```tsx
+export const extraRowActions = (
+ ctx: ProductsPageContext,
+ record: Parameters>[1],
+): ProductRowAction[] => [
+ {
+ key: "duplicate",
+ label: "Duplicate",
+ onClick: () => {
+ // your duplication logic here
+ },
+ },
+];
+```
+
+## Form Hooks
+
+The `CreateOrEdit*Modal.custom.tsx` file provides three hook functions for the create/edit form:
+
+```tsx
+import type { ReactNode } from "react";
+import type { CreateOrEditProductFormContext } from "../customizations.types";
+import type { CreateOrEditProductDto } from "@api/generated/service-proxies";
+
+/** Form items rendered after the generated ones. */
+export const extraFormFields = (
+ ctx: CreateOrEditProductFormContext,
+): ReactNode => null;
+
+/**
+ * Runs after validation, just before the record is sent to the server.
+ * Adjust `dto` in place to add computed values.
+ * Throw to cancel the save.
+ */
+export const beforeSave = async (
+ ctx: CreateOrEditProductFormContext,
+ dto: CreateOrEditProductDto,
+): Promise => {};
+
+/** Runs after the record is saved and before the modal closes. */
+export const afterSave = async (
+ ctx: CreateOrEditProductFormContext,
+ dto: CreateOrEditProductDto,
+): Promise => {};
+```
+
+The generated `CreateOrEditProductModal.tsx` calls these hooks:
+
+- `extraFormFields` is rendered after the generated form fields inside the `
+
+
+);
+```
+
+### Example - Modifying DTO Before Save
+
+```tsx
+export const beforeSave = async (
+ ctx: CreateOrEditProductFormContext,
+ dto: CreateOrEditProductDto,
+): Promise => {
+ // Set a computed field before sending to the server
+ dto.slug = dto.name?.toLowerCase().replace(/\s+/g, "-");
+};
+```
+
+## Detail View Hooks
+
+The `View*Modal.custom.tsx` file provides one hook function for the detail/view modal:
+
+```tsx
+import type { ReactNode } from "react";
+import type { ViewProductContext } from "../customizations.types";
+
+/** Detail items rendered after the generated ones. */
+export const extraDetailItems = (ctx: ViewProductContext): ReactNode => null;
+```
+
+### Example - Adding Custom Detail Fields
+
+```tsx
+export const extraDetailItems = (ctx: ViewProductContext): ReactNode => (
+ <>
+
+ {ctx.record.product?.margin ? `${ctx.record.product.margin}%` : "-"}
+
+ >
+);
+```
+
+## Context Objects
+
+Each hook receives a typed context object. The context provides access to useful values without coupling your code to the generated component's internals.
+
+### Page Context
+
+`ProductsPageContext`, passed to every hook in `customizations.tsx`:
+
+| Property | Type | Description |
+|---|---|---|
+| `refresh` | `() => void` | Reloads the table data. Call it after anything that changes data |
+| `isGranted` | `(permission: string) => boolean` | Checks a permission, same check the generated page uses |
+| `navigate` | React Router `NavigateFunction` | Navigates to a route |
+| `service` | `ProductsServiceProxy` | The entity's service proxy, ready to call |
+
+### Form Context
+
+`CreateOrEditProductFormContext`, passed to every hook in `CreateOrEdit*Modal.custom.tsx`:
+
+| Property | Type | Description |
+|---|---|---|
+| `form` | Ant Design `FormInstance` | The form instance for reading/setting field values |
+| `isEditMode` | `boolean` | `false` while creating, `true` while editing an existing record |
+| `productId` | Primary key type or `undefined` | The record's primary key, `undefined` while creating |
+| `service` | `ProductsServiceProxy` | The entity's service proxy, ready to call |
+
+> The primary key property is named after the entity - `productId` for a `Product`, `orderId` for an
+> `Order` - not `entityId`.
+
+### Detail Context
+
+`ViewProductContext`, passed to `extraDetailItems`:
+
+| Property | Type | Description |
+|---|---|---|
+| `record` | `GetProductForViewDto` | The record being displayed. Not optional - the hook is called at a point where the record is already loaded |
+
+> `GetProductForViewDto` wraps the entity, so the entity's own properties are one level down:
+> `ctx.record.product.name`, not `ctx.record.name`.
+
+## Safety Features
+
+- **SkipIfExists:** Customization files (`customizations.tsx`, `*.custom.tsx`) are generated once and never overwritten, even on regeneration.
+- **Type safety:** `customizations.types.ts` is regenerated to keep types in sync. If the entity changes shape, stale code in your customization files produces a compile error rather than breaking silently.
+- **Ownership guard:** Toggling the **Generate Overridable Entity** switch on an entity that already has
+ generated files is handled in both directions:
+ - Turning it **off** points a fully generated template back at the file that holds your code. Power Tools
+ detects your code and refuses to overwrite it.
+ - Turning it **on** makes a path that used to be fully generated developer-owned. The file already sitting
+ there is old generated code, so Power Tools **deletes it** and lets the template write the new split -
+ saving a copy as `.bak` beside it first. If you had hand-edited that file, your changes are in
+ the `.bak`, not in the regenerated one.
+
+## Next
+
+[How to Create & Edit Power Tools Templates](How-To-Create-Edit-Power-Tools-Templates.md)
diff --git a/docs/en/images/aspnet-zero-power-tools-generate-overridable-entity.png b/docs/en/images/aspnet-zero-power-tools-generate-overridable-entity.png
new file mode 100644
index 00000000..e14f2be9
Binary files /dev/null and b/docs/en/images/aspnet-zero-power-tools-generate-overridable-entity.png differ
diff --git a/docs/en/nav-aspnet-core-angular.json b/docs/en/nav-aspnet-core-angular.json
index 1d976418..1291dbf5 100644
--- a/docs/en/nav-aspnet-core-angular.json
+++ b/docs/en/nav-aspnet-core-angular.json
@@ -581,6 +581,14 @@
"text": "Master Detail Tables",
"path": "Power-Tools-Master-Detail-Tables.md"
},
+ {
+ "text": "Custom Code",
+ "path": "Power-Tools-Custom-Code-Angular.md"
+ },
+ {
+ "text": "Custom Code - MAUI",
+ "path": "Power-Tools-Custom-Code-Maui.md"
+ },
{
"text": "How to Create & Edit Power Tools Templates",
"path": "How-To-Create-Edit-Power-Tools-Templates.md"
diff --git a/docs/en/nav-aspnet-core-mvc.json b/docs/en/nav-aspnet-core-mvc.json
index 4f648527..a70d8723 100644
--- a/docs/en/nav-aspnet-core-mvc.json
+++ b/docs/en/nav-aspnet-core-mvc.json
@@ -617,6 +617,14 @@
"text": "Master Detail Tables",
"path": "Power-Tools-Master-Detail-Tables.md"
},
+ {
+ "text": "Custom Code",
+ "path": "Power-Tools-Custom-Code-Mvc.md"
+ },
+ {
+ "text": "Custom Code - MAUI",
+ "path": "Power-Tools-Custom-Code-Maui.md"
+ },
{
"text": "How to Create & Edit Power Tools Templates",
"path": "How-To-Create-Edit-Power-Tools-Templates.md"
diff --git a/docs/en/nav-aspnet-core-react.json b/docs/en/nav-aspnet-core-react.json
index 93e78ce6..e806cc84 100644
--- a/docs/en/nav-aspnet-core-react.json
+++ b/docs/en/nav-aspnet-core-react.json
@@ -569,6 +569,14 @@
"text": "Master Detail Tables",
"path": "Power-Tools-Master-Detail-Tables.md"
},
+ {
+ "text": "Custom Code",
+ "path": "Power-Tools-Custom-Code-React.md"
+ },
+ {
+ "text": "Custom Code - MAUI",
+ "path": "Power-Tools-Custom-Code-Maui.md"
+ },
{
"text": "How to Create & Edit Power Tools Templates",
"path": "How-To-Create-Edit-Power-Tools-Templates.md"