Migrate from Angular Material
Material gives you primitives to assemble; OGE components carry the behaviour themselves. The move is mostly deletion: the MatTableDataSource, sort and paginator wiring becomes one grid, and mat-form-field wrappers become editors with their own label and validation.
Component map
Each Angular element has a React twin with the same options (<OgeGrid>, <OgeSelectBox>…).
| Angular Material | OGE | Package |
|---|---|---|
mat-table + MatSort + mat-paginator | oge-grid (or oge-simple-table for a static table) | @oge-ui/grid · @oge-ui/layout |
mat-tree | oge-tree-view | @oge-ui/navigation |
MatDialog | OgeModalService.open() · oge-modal | @oge-ui/overlay |
MatSnackBar | OgeToastService | @oge-ui/overlay |
mat-menu + matMenuTriggerFor | oge-drop-down-button · [ogeContextMenu] · oge-menu-list | @oge-ui/buttons · @oge-ui/overlay |
matTooltip | [ogeTooltip] | @oge-ui/overlay |
mat-button · mat-flat-button · mat-stroked-button · mat-icon-button | oge-button ( stylingMode: 'text' | 'contained' | 'outlined' ) | @oge-ui/buttons |
mat-button-toggle-group | oge-button-group | @oge-ui/buttons |
mat-select | oge-select-box ( oge-tag-box for multiple) | @oge-ui/inputs |
mat-autocomplete | oge-autocomplete | @oge-ui/inputs |
mat-datepicker | oge-date-box · oge-date-range-box | @oge-ui/inputs |
matInput in mat-form-field | oge-text-box · oge-number-box · oge-text-area | @oge-ui/inputs |
mat-checkbox · mat-radio-group · mat-slide-toggle | oge-check-box · oge-radio-group · oge-switch | @oge-ui/inputs |
mat-slider | oge-slider · oge-range-slider | @oge-ui/inputs |
mat-tab-group | oge-tabs | @oge-ui/tabs |
mat-stepper | oge-stepper | @oge-ui/navigation |
mat-sidenav | oge-drawer | @oge-ui/navigation |
mat-expansion-panel · mat-accordion | oge-expansion-panel · oge-accordion | @oge-ui/layout |
mat-card · mat-chip-set · mat-progress-bar · mat-progress-spinner | oge-card · oge-chip-list · oge-progress-bar · oge-load-indicator | @oge-ui/layout |
mat-badge | badge input on oge-button , or oge-badge | @oge-ui/buttons · @oge-ui/layout |
Defaults that differ
| Behaviour | Angular Material | OGE |
|---|---|---|
| Snack bar / toast dismisses itself | no — stays until dismissed unless duration is set | yes — after toastDisplayTime (4000 ms) |
| Tooltip side | matTooltipPosition = below | tooltipPlacement = top |
| Tooltip delay | matTooltipShowDelay = 0 | tooltipShowDelayMs = 400 ms |
| Paging | only with a mat-paginator you connect | paging on the grid; without it the grid scrolls (virtual when scrolling.mode is virtual ) |
| Sorting | only with matSort + mat-sort-header | on for every column with a field ; sortable turns a column off |
| Filtering | a filterPredicate you write | filterRow , headerFilter , searchPanel — accent- and case-insensitive |
Tables
A Material table renders what you give it; the grid owns sorting, paging, filtering, grouping, selection, editing and export, in memory or through a server data source (see the filter contract). For a plain styled table with no behaviour, use oge-simple-table.
<!-- Material: a table you assemble — columns, sort and paginator wired by hand -->
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let row">{{ row.name }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="['name']"></tr>
<tr mat-row *matRowDef="let row; columns: ['name']"></tr>
</table>
<mat-paginator [pageSize]="20" />
<!-- OGE: the grid owns sorting, paging, filtering and virtual scrolling -->
<oge-grid [data]="rows" keyField="id" [paging]="{ pageSize: 20 }" [filterRow]="true">
<oge-column field="name" caption="Name" />
</oge-grid>
<!-- a static, styled table without grid behaviour -->
<oge-simple-table [value]="rows" [columns]="[{ field: 'name', caption: 'Name' }]" />Dialogs and snack bars
Both open components the same way. The OGE modal ref resolves a promise instead of exposing an observable, and the dialog closes on a backdrop click and Escape like Material's (closeOnBackdropClick, closeOnEscape).
// Material: MatDialog.open(Component, { data }) → afterClosed() observable
const ref = this.dialog.open(EditUser, { data: user, width: '480px' });
ref.afterClosed().subscribe((result) => …);
// OGE: OgeModalService.open(Component, { data }) → a ref whose closed promise carries the result
const ref = this.modals.open(EditUser, { data: user, width: 480 });
ref.closed.then(({ result }) => …);
// inside EditUser: inject(OGE_MODAL_DATA), inject(OgeModalRef).close(value)// Material: MatSnackBar — stays until dismissed unless you pass a duration
this.snack.open('Saved', 'Undo', { duration: 3000 });
// OGE: OgeToastService — dismisses itself after 4 s by default, actions included
this.toasts.success('Saved', { action: { text: 'Undo', handler: () => this.undo() } });Form fields
OGE editors bring their own chrome: label, labelMode, hint and error text, and they implement ControlValueAccessor, so reactive and template-driven forms keep working unchanged.
<!-- Material: mat-form-field wraps a native control -->
<mat-form-field>
<mat-label>City</mat-label>
<mat-select [(value)]="city">
@for (c of cities; track c) { <mat-option [value]="c">{{ c }}</mat-option> }
</mat-select>
</mat-form-field>
<!-- OGE: the editor carries its own label, chrome and validation messages -->
<oge-select-box label="City" [items]="cities" [(value)]="city" />Theming
Material's Sass theme maps onto the --oge-* tokens: set --oge-accent to your primary colour and --oge-radius to your shape scale, or import a bridge. Dark mode follows your own switch through dark-class.css or the Sass theme() mixin — see Styling.