- Getting Started
- Architecture
- The Basics
- Digging Deeper
-
EDGE Components
- Introduction
- Accordion
- Activity Indicator
- Badge
- Bottom Navigation
- Bottom Sheet
- Button
- Button Group
- Canvas
- Carousel
- Checkbox
- Chip
- Column
- Divider
- Floating Action Button
- Gesture Area
- Icon
- Image
- Lazy Grid
- Layout & Styling
- List
- Menus
- Modal
- Pressable
- Progress Bar
- Radio Group
- Pager
- Refreshable
- Row
- Scroll View
- Select
- Shapes
- Side Navigation
- Slider
- Spacer
- Stack
- Tab Row
- Text
- Text Input
- Toggle
- Top Bar
- Virtual List
- Web View
- Plugins
- Testing
- Publishing Your App
List
Get the Jump app
You'll need the free Jump app to preview this page on your device. Install it, then scan the code again.
Overview#
A virtualized list container. On iOS, renders as a SwiftUI List with native pull-to-refresh and trailing
swipe-to-delete. On Android, renders as a LazyColumn / LazyRow.
Pair with <native:list-item> for Material3 list rows, or use any EDGE element as a child.
<native:list separator on-refresh="refresh" on-end-reached="loadMore"> @foreach($contacts as $contact) <native:list-item headline="{{ $contact->name }}" supporting="{{ $contact->email }}" leadingMonogram="{{ $contact->initials }}" trailingIcon="forward" @press="openContact({{ $contact->id }})" /> @endforeach</native:list>
Props#
horizontal- Lay out children horizontally instead of vertically (optional, boolean, default:false)shows-indicators- Show scroll indicators (optional, boolean, default:false) [iOS]separator- Render dividers between rows (optional, boolean, default:false)plain- Force a flat, ungrouped list. By default a list containing<native:list-section>children adopts the inset-grouped style (rounded cards — iOS.insetGrouped, grouped cards on Android);plainkeeps flat rows with plain section headers instead (optional, boolean, default:false)on-refresh- Component method called on pull-to-refresh (optional, string) [iOS]on-end-reached- Component method called when the user nears the end of the list (optional, string)end-reached-buffermobile-ui 0.7 - Number of items from the end at whichon-end-reachedis triggered (optional, integer, default:3)
Children#
Accepts any EDGE elements as children. <native:list-item> is the canonical child for Material3-style rows.
List Item#
A pre-styled Material3 row with a headline, optional supporting + overline text, and configurable leading + trailing content slots.
<native:list-item headline="Inbox" supporting="42 unread" leadingIcon="email" trailingIcon="forward" @press="openInbox"/>
Text props#
headline- Primary text (required, string)supporting- Secondary text rendered below the headline (optional, string)overline- Small caption rendered above the headline (optional, string)headline-line-through- Strike through the headline, e.g. for a completed todo (optional, boolean, default:false). Bind it::headline-line-through="$todo->done". Not innativephp/mobile-ui0.6.0; it needs the release that includes NativePHP/mobile-ui#109
Leading slot (mutually exclusive)#
leadingIcon- Icon name rendered as a leading icon. Pair withleadingIconIos/leadingIconAndroidto override the icon per platformleadingAvatar- URL of a circular avatar imageleadingMonogram- 1-2 character monogram (combine withleadingMonogramColor)leadingMonogramColor- Hex color for monogram backgroundleadingImage- URL of a square image with a small radiusleadingImageFit- HowleadingImagefits its square:coverfills and crops (default);containshows the whole image while preserving its aspect ratio. For logos or wide images, setleadingImageFit="contain". Works on iOS and Android;leading-image-fitis also acceptedleadingCheckbox- Boolean value for a leading checkbox (bind it::leadingCheckbox="$done"). Interactive whenon-leading-changeis set — tapping the box fires your handler with the new value (the row's own@pressstill handles taps elsewhere on the row); without a handler it renders as a static state glyphleadingRadio- Boolean value for a leading radio button. Interactive whenon-leading-changeis set; static glyph otherwise
Trailing slot (mutually exclusive)#
trailingIcon- Icon name rendered as a trailing icon. Pair withtrailingIconIos/trailingIconAndroidto override the icon per platformtrailingText- Trailing text labeltrailingCheckbox- Boolean value for a trailing checkbox. Interactive whenon-trailing-changeis set; static glyph otherwisetrailingSwitch- Boolean value for a trailing switch [Android]trailingIconButton- Icon name for a tappable trailing button. Handle taps withon-trailing-press(see Events)trailing-a11y-label- Accessibility label for the trailing icon button (recommended whenevertrailingIconButtonis set). See Accessibilitytrailing-menu- Attach a tap-to-open dropdown to the row's trailing edge (trailingMenualso works). When set without an explicit trailing slot, anellipsisicon button is auto-created as the anchor. See Menus
Independent of the mutually-exclusive slot above, a row can also show a stack of small status icons:
trailing-badges- An array of small status badges drawn right-aligned, so several can show at once (e.g. a flag and a pin). Each badge is['icon' => ..., 'ios' => ..., 'android' => ..., 'color' => 'red-500'], whereiconis a shared icon name,ios/androidoverride it per platform, andcolortakes any color value.
Color overrides#
All color props accept the full color grammar — hex (including
#RRGGBBAA alpha), Tailwind palette names, and /N opacity modifiers (red-300/20).
headlineColor,supportingColor,overlineColor- Colors for the text stylescontainerColor- Row background colorleadingIconColor,trailingIconColor,trailingTextColor- Colors for the slot contentleadingIconBgColor- Background color of the leading icon's circle
State and accessibility#
disabled- Disable the row (optional, boolean, default:false)a11y-label/a11y-hint- Screen-reader label and hint for the whole row. See AccessibilitytonalElevation- Tonal elevation in dp [Android]shadowElevation- Shadow elevation in dp [Android]
Events#
-
@press/@longPress- Standard press handlers on the row -
on-swipe-delete- Shortcut for a single destructive trailing swipe. For anything richer, usetrailing-actionsbelow. -
on-leading-change/on-trailing-change- Component method called when the leading/trailing checkbox or radio is toggled, receiving the new value. Without a handler the control renders as a static state glyph. -
on-trailing-press- Component method called when the trailing icon button is tapped, on both platforms. This attribute needs thenativephp/mobile-uirelease that includes NativePHP/mobile-ui#108. In 0.6.0 it is silently ignored and only the fluent->onTrailingPress()on aListItemelement works.@trailing-pressnever works: unknown@nameattributes are treated as child-component events and dropped from plain elements
The trailing switch is interactive on [Android] only.
Handlers receive their arguments in this order: the arguments you wrote in the expression, then the value the control
sends. So on-leading-change="toggleTask({{ $task->id }})" calls toggleTask($id, $checked):
public function toggleTask(int $id, bool $checked): void{ Task::whereKey($id)->update(['done' => $checked]);}
on-swipe-delete and @press send no value, so on-swipe-delete="deleteTask({{ $task->id }})" calls
deleteTask($id). To drive these from a test, see Testing a list.
Swipe actions#
Configure swipe actions on either edge with leading-actions and trailing-actions — each an array of action
definitions the user reveals by swiping the row. Each action is an array:
method- Component method to call when the action is tapped (required)label- The action's textios/android- The action icon, resolved per platform (oriconfor a shared name)tint- Background color as a hex stringrole- Set todestructiveto render in the platform's delete style (trailing only)
Swipe actions only work on rows that are direct children of <native:list> (or a <native:list-section>) —
they are attached by the list renderer, so a standalone <native:list-item> silently ignores them.
<native:list> @foreach ($emails as $email) <native:list-item :headline="$email->subject" :leading-actions="[ ['method' => 'toggleRead('.$email->id.')', 'label' => 'Read', 'ios' => 'envelope.open', 'android' => 'mark_email_read', 'tint' => '#3B82F6'], ]" :trailing-actions="[ ['method' => 'flag('.$email->id.')', 'label' => 'Flag', 'ios' => 'flag', 'android' => 'flag', 'tint' => '#F97316'], ['method' => 'delete('.$email->id.')', 'label' => 'Delete', 'ios' => 'trash', 'android' => 'delete', 'role' => 'destructive'], ]" /> @endforeach</native:list>
List Section#
Group rows under a header (and optional footer) with <native:list-section> — a SwiftUI Section on iOS, a
sticky-header group on Android. Place <native:list-item> children inside; a section on its own renders nothing.
A list that contains sections automatically adopts the inset-grouped style (rounded cards); pass plain to the
list to keep flat rows with plain section headers instead.
<native:list> <native:list-section header="Fruits" footer="2 items"> <native:list-item headline="Apple" /> <native:list-item headline="Banana" /> </native:list-section> <native:list-section header="Vegetables"> <native:list-item headline="Carrot" /> </native:list-section></native:list>
header- Section header textfooter- Optional footer text below the section
use Native\Mobile\UI\Elements\ListSection; ListSection::make('Fruits', ListItem::make('Apple'))->footer('1 item');
ListSection methods#
make(string $header = '', Element ...$children)- Create a section with a header and rowsheader(string $text)- Set the section header textfooter(string $text)- Set the optional footer text
Examples#
Settings menu#
<native:list separator> <native:list-item headline="Profile" leadingIcon="person" trailingIcon="forward" @press="openProfile" /> <native:list-item headline="Notifications" leadingIcon="notifications" trailingIcon="forward" @press="openNotifications" /> <native:list-item headline="Privacy" leadingIcon="lock" trailingIcon="forward" @press="openPrivacy" /> <native:list-item headline="Help" leadingIcon="help" trailingIcon="forward" @press="openHelp" /></native:list>
Swipe-to-delete with pull-to-refresh#
The checkbox, swipe, and row press are three independent targets on one row: tapping the box fires
on-leading-change, swiping left fires on-swipe-delete, tapping anywhere else fires @press.
<native:list separator on-refresh="refreshTasks"> @foreach($tasks as $task) <native:list-item headline="{{ $task->title }}" supporting="{{ $task->due }}" :leadingCheckbox="$task->done" on-leading-change="toggleTask({{ $task->id }})" trailingIcon="forward" on-swipe-delete="deleteTask({{ $task->id }})" @press="openTask({{ $task->id }})" /> @endforeach</native:list>
Note
Pull-to-refresh needs to own the pull gesture, so it only fires when the list is the screen's scrolling container — inside another scroll view (like this docs page) the outer container wins the pull. Swipe and checkbox work inline; run the refresh on a dedicated screen.
Infinite scroll#
loadMore() is a method on your component that fetches the next page and appends it to the collection the loop
renders — in a real app the loop is @foreach($posts as $post) over your paginated results. The fixed range()
here just gives the demo enough rows to scroll before the end-reached trigger fires.
<native:list on-end-reached="loadMore" :end-reached-buffer="5"> @foreach (range(1, 15) as $i) <native:list-item headline="Post {{ $i }}" supporting="Keep scrolling — loadMore() fires near the end" /> @endforeach</native:list>
A buffer of 5 triggers loadMore() when the list is within five items of the end. When omitted, the buffer defaults to 3.
Testing a list#
In a component test, target a row's callbacks by the expression you wrote in the template. For the swipe-to-delete example above:
use App\NativeComponents\Tasks;use Native\Mobile\Testing\Native; it('completes and deletes a task', function () { Native::test(Tasks::class) ->check('toggleTask(1)') // tap the leading checkbox: calls toggleTask(1, true) ->press('deleteTask(1)') // swipe-to-delete fires a press at its callback ->press('openTask(2)'); // tap the row});
Element#
use Native\Mobile\UI\Elements\NativeList;use Native\Mobile\UI\Elements\ListItem; NativeList::make( ListItem::make('Profile')->leadingIcon('person')->trailingIcon('forward'), ListItem::make('Settings')->leadingIcon('settings')->trailingIcon('forward'),) ->separator() ->onRefresh('refresh') ->onEndReached('loadMore') ->endReachedBuffer(5);
NativeList methods#
make(Element ...$children)- Create a list with childrenhorizontal(bool $value = true)- Horizontal layoutshowsIndicators(bool $value = true)- Show scroll indicatorsseparator(bool $value = true)- Render dividers between rowsonRefresh(string $method)- Pull-to-refresh handleronEndReached(string $method)- End-reached handlerendReachedBuffer(int $items)- Number of items from the end at which the end-reached handler is triggered (default:3)
ListItem methods#
Text:
make(string $headline = ''),supporting(string $text),overline(string $text)
Leading slot:
leadingIcon(?string $name = null, IosSymbol|string|null $ios = null, AndroidSymbol|string|null $android = null)leadingAvatar(string $url)leadingMonogram(string $initials, ?string $color = null)leadingImage(string $url, ?string $fit = null)-cover(default) orcontain, e.g.->leadingImage($url, 'contain')leadingCheckbox(bool $checked = false)leadingRadio(bool $selected = false)
Trailing slot:
trailingIcon(?string $name = null, IosSymbol|string|null $ios = null, AndroidSymbol|string|null $android = null)trailingText(string $text)trailingCheckbox(bool $checked = false)trailingSwitch(bool $checked = false)trailingIconButton(?string $name = null, IosSymbol|string|null $ios = null, AndroidSymbol|string|null $android = null)trailingA11yLabel(string $label)- Accessibility label for the trailing icon button
Swipe actions & badges:
leadingActions(array $actions),trailingActions(array $actions)- Arrays of swipe-action definitions (method,label,ios/android,tint, androlefor trailing)trailingBadges(array $badges)- Stacked right-aligned status icons; each badge is['icon' => ..., 'ios' => ..., 'android' => ..., 'color' => '#hex']
Styling:
headlineColor,supportingColor,overlineColor,containerColor,leadingIconColor,leadingIconBackgroundColor,trailingIconColor,trailingTextColor(all(string $color))tonalElevation(float $dp),shadowElevation(float $dp)
Callbacks:
onLeadingChange(string $method),onTrailingChange(string $method),onTrailingPress(string $method),onSwipeDelete(string $method)disabled(bool $disabled = true)
in no time