Actions
Button
Trigger an action with clear loading and disabled states.
Import
import { NeuButtonComponent } from '@neural-ui/native/button';Examples
Import ExampleComponent into its parent and place <app-example /> inside your application’s root neu-theme. The preview supplies the device frame and safe areas; use the root setup from Installation in your app.
Project actions
Edit a project name, preview the draft and save your changes. The primary action is available only when there is something to save.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { NeuInputComponent } from '@neural-ui/native/input';
import { NeuBadgeComponent } from '@neural-ui/native/badge';
import { NeuModalComponent } from '@neural-ui/native/modal';
import { View, Text, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, NeuInputComponent, NeuBadgeComponent, NeuModalComponent, View, Text, ScrollView],
template: `
<scroll-view class="product-scroll" keyboardShouldPersistTaps="handled" [showsVerticalScrollIndicator]="true"><view class="product-stack"><view class="product-section"><text class="product-title" accessibilityRole="header">Make it yours</text><text class="product-caption">Product workspace</text></view>
<view class="product-surface"><neu-input label="Project name" [(value)]="name" placeholder="Name your project" />
<text class="product-caption">A clear name helps your team find the right work.</text>
<view class="product-actions"><neu-button label="Save changes" size="sm" [disabled]="!name().trim() || name().trim() === savedName()" (pressed)="savedName.set(name().trim())" /><neu-button label="Preview" size="sm" variant="text" (pressed)="preview.set(true)" /></view>
<text class="product-caption" accessibilityLiveRegion="polite">{{ name().trim() === savedName() ? 'All changes saved' : 'Unsaved changes' }}</text></view>
<neu-modal title="Project preview" [(open)]="preview"><view class="product-section"><neu-badge label="Draft" tone="neutral" /><text class="product-heading">{{ name().trim() || 'Untitled project' }}</text><text>Invite your team and plan the first milestone.</text></view></neu-modal></view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
:host { flex: 1; flex-shrink: 1; min-height: 0; }
.product-scroll { flex: 1; flex-shrink: 1; height: 0px; min-height: 0; }
.product-stack { gap: 24px; padding-bottom: 20px; }
.product-section { gap: 12px; }
.product-row { flex-direction: row; align-items: center; gap: 12px; }
.product-copy { flex: 1; min-width: 0; gap: 4px; }
.product-title { color: var(--neu-text, #172033); font-size: 24px; line-height: 30px; font-weight: 600; }
.product-heading { color: var(--neu-text, #172033); font-size: 17px; line-height: 23px; font-weight: 600; }
.product-caption { color: var(--neu-muted, #596477); font-size: 14px; line-height: 20px; }
.product-actions { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; }
.product-surface { gap: 16px; padding: 16px; border-radius: 16px; background-color: var(--neu-surface, #ffffff); border-width: 1px; border-color: var(--neu-border, #a5afbf); }
`,
})
export class ExampleComponent {
readonly name = signal('Mobile launch');
readonly savedName = signal('Mobile launch');
readonly preview = signal(false);
}Variants
Compare four visual treatments and press any action to update the count.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { View, Text, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, View, Text, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<view class="usage-buttons">
<neu-button label="Save changes" variant="solid" (pressed)="saved.set(saved() + 1)" />
<neu-button label="Preview" variant="tonal" (pressed)="saved.set(saved() + 1)" />
<neu-button label="Cancel" variant="outline" (pressed)="saved.set(saved() + 1)" />
<neu-button label="Learn more" variant="text" (pressed)="saved.set(saved() + 1)" />
</view><text class="usage-caption">Actions: {{ saved() }}</text>
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
.usage-buttons { gap: 12px; align-items: flex-start; }
`,
})
export class ExampleComponent {
readonly saved = signal(0);
}Semantic colors
Compare the six semantic colors supplied by the theme.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { View, Text, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, View, Text, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<view class="usage-buttons">@for (tone of tones; track tone) {
<neu-button [label]="tone" [tone]="tone" (pressed)="saved.set(saved() + 1)" />
}</view><text class="usage-caption">Actions: {{ saved() }}</text>
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
.usage-buttons { gap: 12px; align-items: flex-start; }
`,
})
export class ExampleComponent {
readonly saved = signal(0);
readonly tones = ['brand', 'neutral', 'success', 'info', 'warning', 'danger'] as const;
}Sizes
Compare sm, md and lg with the same label.
View and copy the complete example
import { Component } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { View, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, View, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<view class="usage-buttons"><neu-button label="Continue" size="sm" /><neu-button label="Continue" size="md" /><neu-button label="Continue" size="lg" /></view>
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
.usage-buttons { gap: 16px; align-items: flex-start; }
`,
})
export class ExampleComponent {
}Loading and disabled
Start or stop the saving state and compare it with an unavailable action.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { View, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, View, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<view class="usage-buttons"><neu-button label="Saving changes" [loading]="saving()" /><neu-button label="Unavailable action" [disabled]="true" /><neu-button label="Toggle loading" variant="text" (pressed)="saving.set(!saving())" /></view>
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
.usage-buttons { gap: 12px; align-items: flex-start; }
`,
})
export class ExampleComponent {
readonly saving = signal(true);
}Full width
Let the primary action fill its available container width.
View and copy the complete example
import { Component } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { View, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, View, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<neu-button label="Continue" [fullWidth]="true" /><neu-button label="Cancel" variant="text" />
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
`,
})
export class ExampleComponent {
}Change the settings and try the result.
Device captures shown in English.


API reference
Inputs
| Name | Type | Default | Binding | Description |
|---|---|---|---|---|
| label | string | Required | Input | Human-readable name for the control, action or group. |
| accessibilityLabel | string | '' | Input | Accessible action name. When empty, Button uses its visible label. |
| variant | NeuVariant | 'solid' | Input | Visual hierarchy: solid, tonal, outline or text. Colors come from the surrounding theme. |
| tone | NeuTone | 'brand' | Input | Semantic color: neutral, brand, success, info, warning or danger. The accent value remains a compatible alias for brand. Colors use NeuTheme tokens; explain statuses with words as well as color. |
| size | NeuSize | 'md' | Input | The sm, md and lg sizes have minimum heights of 48, 56 and 64 points and label sizes of 14, 16 and 18 points. Larger text can increase height. Use fullWidth to fill the parent width. |
| fullWidth | boolean | false | Input | Fill the available width of the parent container. The false setting restores the natural content width; size still controls height and typography. |
| disabled | boolean | false | Input | Disables user interaction. Values can still be supplied by the application. |
| loading | boolean | false | Input | Shows the native loading indicator and prevents Button or IconButton activation. |
Models
This component declares no two-way models.
Outputs
| Name | Payload | When it emits |
|---|---|---|
| pressed | TouchEvent | Emits the native press event when an enabled action is activated. |
Types and data interfaces
Use the imports in your application. The definitions below are provided for reference.
NeuVariant
import type { NeuVariant } from '@neural-ui/native/foundation';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuVariant = 'solid' | 'tonal' | 'outline' | 'text';NeuTone
import type { NeuTone } from '@neural-ui/native/foundation';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuTone = 'neutral' | 'brand' | 'accent' | 'success' | 'info' | 'warning' | 'danger';NeuSize
import type { NeuSize } from '@neural-ui/native/foundation';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuSize = 'sm' | 'md' | 'lg';TouchEvent
import type { TouchEvent } from '@ng-native/components';
import type { NativeSyntheticEvent } from '@ng-native/fabric';
import type { TouchEventPayload } from '@ng-native/components';
// Public definition for reference only. Import this type; do not redeclare it.
export type TouchEvent = NativeSyntheticEvent<TouchEventPayload>;NativeSyntheticEvent
import type { NativeSyntheticEvent } from '@ng-native/fabric';
// Public definition for reference only. Import this type; do not redeclare it.
export interface NativeSyntheticEvent<T = unknown> {
readonly nativeEvent: T;
/**
* The node the event happened on, which a listener further up can compare with its own: RN's
* `event.target`. Null where a host has no node to name.
*/
readonly target?: unknown;
stopPropagation(): void;
isPropagationStopped(): boolean;
}TouchEventPayload
import type { TouchEventPayload } from '@ng-native/components';
import type { TouchPayload } from '@ng-native/fabric';
// Public definition for reference only. Import this type; do not redeclare it.
export interface TouchEventPayload extends TouchPayload {
readonly touches?: readonly TouchPayload[];
readonly changedTouches?: readonly TouchPayload[];
}TouchPayload
import type { TouchPayload } from '@ng-native/fabric';
// Public definition for reference only. Import this type; do not redeclare it.
export interface TouchPayload {
readonly pageX?: number;
readonly pageY?: number;
readonly locationX?: number;
readonly locationY?: number;
readonly identifier?: number;
readonly target?: number;
readonly timestamp?: number;
}