Data & media
Gallery
Display a collection of image thumbnails.
Import
import { NeuGalleryComponent } from '@neural-ui/native/gallery';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.
Landscape studies
Browse a coherent illustration collection. Change the layout or open a thumbnail to inspect the original image.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { NeuGalleryComponent } from '@neural-ui/native/gallery';
import { View, Text, ScrollView } from '@ng-native/components';
import type { NeuGalleryLayout } from '@neural-ui/native/gallery';
import type { NeuImageItem } from '@neural-ui/native';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, NeuGalleryComponent, View, Text, ScrollView],
template: `
<scroll-view class="product-scroll" keyboardShouldPersistTaps="handled" [showsVerticalScrollIndicator]="false"><view class="product-stack"><view class="product-section"><text class="product-title" accessibilityRole="header">Landscape studies</text><text class="product-caption">Four original illustrations · Neural collection</text></view>
<view class="product-actions">
<neu-button label="Grid" size="sm" [variant]="layout() === 'grid' ? 'solid' : 'outline'" (pressed)="layout.set('grid')" />
<neu-button label="Mosaic" size="sm" [variant]="layout() === 'mosaic' ? 'solid' : 'outline'" (pressed)="layout.set('mosaic')" />
</view>
<neu-gallery label="Neural visual collection" [items]="images" [layout]="layout()" [columns]="2" [gap]="12" [aspectRatio]="1" [showCaptions]="true" [(index)]="index" [(open)]="open" /></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 images: readonly NeuImageItem[] = [
{
"id": "forest",
"label": "Forest walk",
"uri": "/assets/media/v2/forest.png"
},
{
"id": "desert",
"label": "Desert shapes",
"uri": "/assets/media/v2/desert.png"
},
{
"id": "night",
"label": "Night sky",
"uri": "/assets/media/v2/night.png"
},
{
"id": "coast",
"label": "Quiet horizon",
"uri": "/assets/media/v2/coast.png"
}
];
readonly index = signal(0);
readonly open = signal(false);
readonly layout = signal<NeuGalleryLayout>('mosaic');
}Custom thumbnails and captions
Customize the thumbnail and caption. Gallery still opens the original image and keeps the grid or mosaic geometry.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuGalleryComponent } from '@neural-ui/native/gallery';
import { View, Text, Image, ScrollView } from '@ng-native/components';
import { NeuGalleryItemDirective, NeuGalleryCaptionDirective } from '@neural-ui/native/gallery';
import type { NeuImageItem } from '@neural-ui/native';
@Component({
selector: 'app-example',
imports: [NeuGalleryComponent, NeuGalleryItemDirective, NeuGalleryCaptionDirective, View, Text, Image, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<neu-gallery label="Travel collection" [items]="images" [columns]="2" [showCaptions]="true" [(open)]="open" [(index)]="index">
<ng-template neuGalleryItem let-photo let-ratio="aspectRatio" let-radius="borderRadius">
<view class="photo-tile"><image class="custom-photo" [source]="{ uri: photo.uri }" [alt]="photo.label" resizeMode="cover" [style.aspectRatio]="ratio" [style.borderRadius]="radius" /><view class="photo-tag"><text class="photo-tag-label">Collection</text></view></view>
</ng-template>
<ng-template neuGalleryCaption let-photo let-index="index"><text class="usage-caption" [numberOfLines]="2">{{ index + 1 }} · {{ photo.label }}</text></ng-template>
</neu-gallery>
</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; }
.photo-tile { position: relative; } .custom-photo { width: 100%; } .photo-tag { position: absolute; left: 8px; bottom: 8px; padding: 5px 8px; border-radius: 6px; background-color: #172033; } .photo-tag-label { color: #ffffff; font-size: 12px; }
`,
})
export class ExampleComponent {
readonly images: readonly NeuImageItem[] = [
{
"id": "forest",
"label": "Forest walk",
"uri": "/assets/media/v2/forest.png"
},
{
"id": "desert",
"label": "Desert shapes",
"uri": "/assets/media/v2/desert.png"
},
{
"id": "night",
"label": "Night sky",
"uri": "/assets/media/v2/night.png"
},
{
"id": "coast",
"label": "Quiet horizon",
"uri": "/assets/media/v2/coast.png"
}
];
readonly index = signal(0);
readonly open = signal(false);
}Change the settings and try the result.
Device captures shown in English.


Personalization
Import these directives together with the component and add them to Component.imports. The examples above include the complete setup.
import { NeuGalleryItemDirective, NeuGalleryCaptionDirective } from '@neural-ui/native/gallery';| Template or region | Context | Purpose |
|---|---|---|
ng-template[neuGalleryItem] | $implicit: NeuImageItem; index: number; aspectRatio: number; borderRadius: number | Replaces the thumbnail. Apply aspectRatio and borderRadius to your image to preserve the layout. The gallery action still opens the original item. |
ng-template[neuGalleryCaption] | $implicit: NeuImageItem; index: number; aspectRatio: number; borderRadius: number | Replaces the caption when showCaptions is true. index is the original position in items, including mosaic layouts. |
Template contexts
import type { NeuGalleryItemContext } from '@neural-ui/native/gallery';let-item reads $implicit; named fields use let-index="index". Use the exported context types for reusable TemplateRef declarations. Keep nested content presentational: the parent retains its accessible action and selection behavior.
API reference
Inputs
| Name | Type | Default | Binding | Description |
|---|---|---|---|---|
| label | string | 'Gallery' | Input | Human-readable name for the control, action or group. |
| viewerLabels | NeuGalleryViewerLabels | {} | Input | Optional text overrides for the internal full-screen viewer: label, closeLabel, previousLabel, nextLabel, zoomLabel, zoomOutLabel and errorLabel. Missing entries retain their English defaults; label falls back to the gallery label. Values update without recreating the gallery. |
| items | readonly NeuImageItem[] | [] | Input | Ordered data for the component. Use the data contract below and stable, unique item IDs. |
| emptyLabel | string | 'No images' | Input | Text or accessible name used for empty state. It does not perform the action by itself. |
| layout | NeuGalleryLayout | 'grid' | Input | The grid layout aligns equal thumbnails in rows; mosaic uses staggered columns with alternating image heights. Both preserve original item indices in the viewer. Mosaic does not provide arbitrary cell spanning. |
| columns | number | 2 | Input | Requested column count, clamped to integers 1–4. The native layout reduces it when the container cannot fit 48-point-wide tiles plus gaps. |
| gap | number | 10 | Input | Space between grid cells or mosaic columns/items, from 0 to 48 layout points. Zero creates a continuous tiled composition. |
| aspectRatio | number | 1 | Input | Base width/height ratio, from 0.25 to 4. 1 creates square grid images; 1.5 creates landscape images. Mosaic applies its alternating height factors to this base. Images use cover without distortion. |
| borderRadius | number | 12 | Input | Corner radius of each thumbnail in layout points. Zero creates square corners for an edge-to-edge mosaic. Does not affect the full-screen viewer. |
| showCaptions | boolean | true | Input | Show each image label below its thumbnail. Hiding captions keeps each tile’s accessible name and its viewer title. |
Models
| Name | Type | Default | Binding | Description |
|---|---|---|---|---|
| index | number | 0 | Model · two-way | Current zero-based image or carousel position. Bind a writable signal with [(index)]. |
| open | boolean | false | Model · two-way | Whether the presentation or message is requested to be open. Bind a writable signal with [(open)]. |
Outputs
| Name | Payload | When it emits |
|---|---|---|
| indexChange | number | Emits the updated index value. [(index)] binds this output together with its model input. |
| openChange | boolean | Emits the updated open value. [(open)] binds this output together with its model input. |
Model state
Bind the signal with [(modelName)] without calling it. Use modelName() to read its value.
import { signal } from '@angular/core';
// Fields inside your component class
readonly index = signal<number>(0);
readonly open = signal<boolean>(false);Types and data interfaces
Use the imports in your application. The definitions below are provided for reference.
NeuGalleryViewerLabels
import type { NeuGalleryViewerLabels } from '@neural-ui/native/gallery';
// Public definition for reference only. Import this type; do not redeclare it.
export interface NeuGalleryViewerLabels {
readonly label?: string;
readonly closeLabel?: string;
readonly previousLabel?: string;
readonly nextLabel?: string;
readonly zoomLabel?: string;
readonly zoomOutLabel?: string;
readonly errorLabel?: string;
}| Field | Type | Requirement | Meaning |
|---|---|---|---|
| label | string | Optional | Accessible name of the full-screen viewer; falls back to the gallery label. |
| closeLabel | string | Optional | Accessible name of the image viewer close action. |
| previousLabel | string | Optional | Accessible name of the previous-image action. |
| nextLabel | string | Optional | Accessible name of the next-image action. |
| zoomLabel | string | Optional | Accessible name of the zoom-in action. |
| zoomOutLabel | string | Optional | Accessible name of the zoom-out action. |
| errorLabel | string | Optional | Message announced when the image cannot be loaded. |
NeuImageItem
import type { NeuImageItem } from '@neural-ui/native';
// Public definition for reference only. Import this type; do not redeclare it.
export interface NeuImageItem {
readonly id: string;
readonly uri: string;
readonly label: string;
}| Field | Type | Requirement | Meaning |
|---|---|---|---|
| id | string | Required | Stable, unique identifier used for tracking and selection. |
| uri | string | Required | Native image or file URI. |
| label | string | Required | Human-readable item name. |
NeuGalleryLayout
import type { NeuGalleryLayout } from '@neural-ui/native/gallery';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuGalleryLayout = 'grid' | 'mosaic';