Screens
AuthScreen
A complete authentication screen with support for login, registration, and OTP flows. Renders a form, optional OAuth provider buttons, and an optional footer slot.
| Prop | Type | Default | Description |
|---|---|---|---|
variant? | 'login' | 'register' | 'otp' | 'login' | Which authentication flow to render |
title? | string | — | Screen heading |
subtitle? | string | — | Text below the heading |
logo? | React.ReactNode | — | Logo rendered above the title |
onSubmit? | (values: Record<string, string>) => void | — | Called when the form is submitted |
onOAuthPress? | (provider: string) => void | — | Called when an OAuth button is pressed |
oauthProviders? | Array<{ key: string; label: string; icon?: string }> | — | List of OAuth provider buttons to render |
footer? | React.ReactNode | — | Slot rendered below the form (e.g. sign-up link) |
loading? | boolean | false | Disables interaction and shows loading state |
accessibilityLabel? | string | — | Accessibility label |
import { AuthScreen } from '@objectifthunes/limestone-sdk';
<AuthScreen
variant="login"
title="Welcome back"
subtitle="Sign in to your account"
oauthProviders={[
{ key: 'google', label: 'Continue with Google', icon: 'google' },
{ key: 'apple', label: 'Continue with Apple', icon: 'apple' },
]}
onSubmit={(values) => signIn(values.email, values.password)}
onOAuthPress={(provider) => signInWithOAuth(provider)}
loading={isSubmitting}
/>
OnboardingScreen
A paginated onboarding flow rendered as a sequence of full-screen slides. Each slide contains a title, description, and optional illustration.
| Prop | Type | Default | Description |
|---|---|---|---|
steps | OnboardingStep[] | — | Ordered array of slides; each has title, description, and optional image |
onComplete? | () => void | — | Called when the user reaches the last step and presses the complete button |
onSkip? | () => void | — | Called when the user presses Skip |
showSkip? | boolean | true | Whether to render the Skip button |
completeLabel? | string | 'Get started' | Label for the button on the final slide |
nextLabel? | string | 'Next' | Label for the next button on intermediate slides |
skipLabel? | string | 'Skip' | Label for the skip button |
accessibilityLabel? | string | — | Accessibility label |
import { OnboardingScreen } from '@objectifthunes/limestone-sdk';
const steps = [
{ title: 'Track anything', description: 'Add tasks, habits, and goals in seconds.' },
{ title: 'Stay in sync', description: 'Everything updates across all your devices.' },
{ title: 'Review and grow', description: 'Weekly summaries keep you on track.' },
];
<OnboardingScreen
steps={steps}
showSkip
onSkip={() => router.replace('/home')}
onComplete={() => router.replace('/home')}
/>
SettingsScreen
A grouped settings list where each row is a toggle, navigation link, or read-only info item. Groups have optional titled headers.
| Prop | Type | Default | Description |
|---|---|---|---|
groups | SettingsGroup[] | — | Array of setting groups; each has an optional title and an array of SettingsItem |
header? | React.ReactNode | — | Optional slot rendered above all groups (e.g. user profile card) |
accessibilityLabel? | string | — | Accessibility label |
Each SettingsItem has: key, label, type ('toggle' | 'navigation' | 'info'), optional value, onPress, onValueChange, icon, and destructive.
import { SettingsScreen } from '@objectifthunes/limestone-sdk';
const groups = [
{
title: 'Account',
items: [
{ key: 'profile', label: 'Edit profile', type: 'navigation', onPress: () => router.push('/profile') },
{ key: 'email', label: 'Email', type: 'info', value: 'alice@example.com' },
],
},
{
title: 'Preferences',
items: [
{ key: 'notifications', label: 'Push notifications', type: 'toggle', value: true, onValueChange: setNotifications },
{ key: 'darkMode', label: 'Dark mode', type: 'toggle', value: false, onValueChange: setDarkMode },
],
},
{
items: [
{ key: 'signOut', label: 'Sign out', type: 'navigation', destructive: true, onPress: signOut },
],
},
];
<SettingsScreen groups={groups} />
ProfileScreen
A user profile screen with avatar, name, email, bio, stats row, and an action list.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Display name (required) |
email? | string | — | Email shown below the name |
avatar? | { src?: string; name?: string } | — | Avatar image URL and/or name for initials fallback |
bio? | string | — | Short biography text |
stats? | Array<{ label: string; value: string }> | — | Numeric stats displayed in a horizontal row |
actions? | Array<{ label: string; onPress: () => void; destructive?: boolean }> | — | Action buttons rendered below the bio |
accessibilityLabel? | string | — | Accessibility label |
import { ProfileScreen } from '@objectifthunes/limestone-sdk';
<ProfileScreen
name="Alice Martin"
email="alice@example.com"
avatar={{ src: 'https://example.com/alice.jpg', name: 'Alice Martin' }}
bio="Product designer & weekend hiker."
stats={[
{ label: 'Posts', value: '142' },
{ label: 'Followers', value: '1.2k' },
{ label: 'Following', value: '98' },
]}
actions={[
{ label: 'Edit profile', onPress: () => router.push('/profile/edit') },
{ label: 'Delete account', onPress: deleteAccount, destructive: true },
]}
/>
ListDetailScreen
A detail screen with an optional hero image, title, subtitle, body content area, and an actions slot at the bottom.
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Primary heading (required) |
subtitle? | string | — | Secondary text below the title |
content? | React.ReactNode | — | Body content rendered in the scrollable area |
headerImage? | React.ReactNode | — | Hero image rendered at the top |
onBack? | () => void | — | Called when the user presses the back affordance |
actions? | React.ReactNode | — | Slot for CTA buttons at the bottom |
accessibilityLabel? | string | — | Accessibility label |
import { ListDetailScreen, Text, Stack } from '@objectifthunes/limestone-sdk';
<ListDetailScreen
title="Getting started"
subtitle="Read time: 5 min"
onBack={() => router.back()}
content={
<Stack gap="md">
<Text variant="body">Install the SDK and wrap your app with LimestoneProvider.</Text>
</Stack>
}
/>
PaywallScreen
A subscription paywall with a plan picker, feature list per plan, purchase button, and restore link.
| Prop | Type | Default | Description |
|---|---|---|---|
plans | Plan[] | — | Available plans; each has key, name, price, period?, features, popular? |
selectedPlan? | string | — | Key of the currently highlighted plan |
onSelectPlan? | (key: string) => void | — | Called when the user taps a plan card |
onPurchase? | () => void | — | Called when the purchase button is pressed |
onRestore? | () => void | — | Called when the restore purchases link is pressed |
title? | string | — | Screen heading |
subtitle? | string | — | Text below the heading |
purchaseLabel? | string | 'Subscribe' | Label for the purchase button |
restoreLabel? | string | 'Restore purchases' | Label for the restore link |
termsText? | string | — | Legal text rendered below the purchase button |
accessibilityLabel? | string | — | Accessibility label |
import { PaywallScreen } from '@objectifthunes/limestone-sdk';
const plans = [
{
key: 'monthly',
name: 'Monthly',
price: '$9.99',
period: 'per month',
features: ['Unlimited projects', 'Priority support'],
},
{
key: 'annual',
name: 'Annual',
price: '$79.99',
period: 'per year',
features: ['Unlimited projects', 'Priority support', '2 months free'],
popular: true,
},
];
<PaywallScreen
title="Upgrade to Pro"
subtitle="Unlock all features"
plans={plans}
selectedPlan={selectedPlan}
onSelectPlan={setSelectedPlan}
onPurchase={purchase}
onRestore={restorePurchases}
/>
ChatScreen
A real-time chat interface with message bubbles, a typing indicator, and a text input bar.
| Prop | Type | Default | Description |
|---|---|---|---|
messages | ChatMessage[] | — | Ordered list of messages; each has id, text, sender ('user' | 'other'), timestamp?, avatar? |
onSendMessage? | (text: string) => void | — | Called when the user submits a message |
placeholder? | string | 'Type a message…' | Input placeholder text |
isTyping? | boolean | false | When true, a typing indicator is shown |
typingLabel? | string | 'Typing…' | Text of the typing indicator |
accessibilityLabel? | string | — | Accessibility label |
import { ChatScreen } from '@objectifthunes/limestone-sdk';
<ChatScreen
messages={messages}
onSendMessage={(text) => sendMessage({ text })}
isTyping={otherUserIsTyping}
placeholder="Say something…"
/>
SearchScreen
A search screen with a search bar, results list, loading state, and an empty state.
| Prop | Type | Default | Description |
|---|---|---|---|
onSearch? | (query: string) => void | — | Called when the query changes (debounced by the search bar) |
results? | unknown[] | [] | Array of result items |
renderResult? | (item: unknown, index: number) => React.ReactNode | — | Row renderer for each result |
keyExtractor? | (item: unknown, index: number) => string | — | Unique key per result row |
placeholder? | string | 'Search…' | Search bar placeholder |
emptyTitle? | string | 'No results' | Heading shown when results is empty |
emptyMessage? | string | — | Supporting text for the empty state |
loading? | boolean | false | Shows a loading indicator instead of results |
accessibilityLabel? | string | — | Accessibility label |
import { SearchScreen, Text, Box } from '@objectifthunes/limestone-sdk';
interface Article { id: string; title: string; }
<SearchScreen
placeholder="Search articles…"
results={articles}
loading={isLoading}
keyExtractor={(item) => (item as Article).id}
renderResult={(item) => (
<Box py="sm" px="md">
<Text variant="body">{(item as Article).title}</Text>
</Box>
)}
onSearch={fetchArticles}
emptyTitle="No articles found"
emptyMessage="Try a different search term."
/>
ErrorScreen
A full-screen error state with an icon, title, message, retry button, and an optional home link.
| Prop | Type | Default | Description |
|---|---|---|---|
title? | string | 'Something went wrong' | Error heading |
message? | string | — | Explanatory text below the heading |
icon? | string | — | Icon name rendered above the title |
retryLabel? | string | 'Try again' | Label for the retry button |
onRetry? | () => void | — | Called when the retry button is pressed |
homeLabel? | string | 'Go home' | Label for the home link |
onHome? | () => void | — | Called when the home link is pressed |
accessibilityLabel? | string | — | Accessibility label |
import { ErrorScreen } from '@objectifthunes/limestone-sdk';
<ErrorScreen
title="Failed to load"
message="Check your connection and try again."
icon="wifi-off"
onRetry={reload}
onHome={() => router.replace('/home')}
/>
ForceUpdateScreen
A blocking screen shown when the installed app version is no longer supported. It renders a message and a single update button; it does not offer a way to dismiss.
| Prop | Type | Default | Description |
|---|---|---|---|
title? | string | 'Update required' | Screen heading |
message? | string | — | Explanation shown below the heading |
icon? | string | — | Icon name rendered above the title |
updateLabel? | string | 'Update now' | Label for the update button |
onUpdate? | () => void | — | Called when the update button is pressed (open the App Store / Play Store) |
accessibilityLabel? | string | — | Accessibility label |
import { ForceUpdateScreen } from '@objectifthunes/limestone-sdk';
<ForceUpdateScreen
title="Update required"
message="This version is no longer supported. Please update to continue."
onUpdate={() => Linking.openURL(APP_STORE_URL)}
/>
NotificationCenterScreen
A full-screen list of in-app notifications grouped by date (Today, Yesterday, Older). Each row shows the notification icon, title, body, and timestamp. Supports mark-as-read and bulk-clear actions.
| Prop | Type | Default | Description |
|---|---|---|---|
notifications | AppNotification[] | — | Ordered list of notifications; each has id, title, body, timestamp, read, icon?, onPress? |
onMarkAllRead? | () => void | — | Called when the user taps “Mark all as read” |
onClearAll? | () => void | — | Called when the user clears all notifications |
onNotificationPress? | (id: string) => void | — | Called when a notification row is tapped |
loading? | boolean | false | Shows a loading skeleton |
accessibilityLabel? | string | — | Accessibility label |
import { NotificationCenterScreen } from '@objectifthunes/limestone-sdk';
<NotificationCenterScreen
notifications={notifications}
onNotificationPress={(id) => handleNotificationTap(id)}
onMarkAllRead={markAllRead}
/>
DashboardScreen
A composable summary screen with a greeting header, a stats grid, and configurable widget slots. Widgets are plain React.ReactNode — drop in any component.
| Prop | Type | Default | Description |
|---|---|---|---|
greeting? | string | — | Heading text (e.g. “Good morning, Alice”) |
stats? | Array<{ label: string; value: string; delta?: string; trend?: 'up' | 'down' | 'neutral' }> | — | KPI cards rendered in a horizontal row |
widgets? | React.ReactNode[] | — | Full-width widget slots rendered below the stats |
header? | React.ReactNode | — | Custom content rendered above the greeting |
refreshing? | boolean | false | Enables pull-to-refresh |
onRefresh? | () => void | — | Called on pull-to-refresh |
accessibilityLabel? | string | — | Accessibility label |
import { DashboardScreen } from '@objectifthunes/limestone-sdk';
<DashboardScreen
greeting="Good morning, Alice"
stats={[
{ label: 'Revenue', value: '$12,400', delta: '+8%', trend: 'up' },
{ label: 'Orders', value: '142', delta: '-3%', trend: 'down' },
]}
widgets={[<RevenueChart key="rev" />, <TopProductsList key="top" />]}
refreshing={isRefreshing}
onRefresh={reload}
/>
ProductDetailScreen
A product page with a media carousel, title, price, description, variant selector, and a sticky add-to-cart bar.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Product name |
images | string[] | — | Array of image URIs for the carousel |
price | number | — | Current price |
currency? | string | 'USD' | ISO 4217 currency code |
originalPrice? | number | — | Original price (renders discount badge) |
description? | string | — | Product description |
variants? | ProductVariant[] | — | Each variant: { key, label, options: string[] } |
selectedVariants? | Record<string, string> | — | Currently selected variant values |
onVariantChange? | (key: string, value: string) => void | — | Called when the user picks a variant |
onAddToCart? | () => void | — | Called when the add-to-cart button is pressed |
onBack? | () => void | — | Back navigation handler |
loading? | boolean | false | Shows a loading skeleton |
accessibilityLabel? | string | — | Accessibility label |
import { ProductDetailScreen } from '@objectifthunes/limestone-sdk';
<ProductDetailScreen
name="Wireless Headphones"
images={['https://example.com/img1.jpg', 'https://example.com/img2.jpg']}
price={89.99}
originalPrice={129.99}
description="Premium audio with 30-hour battery life."
variants={[{ key: 'color', label: 'Color', options: ['Black', 'White', 'Blue'] }]}
selectedVariants={selectedVariants}
onVariantChange={setVariant}
onAddToCart={addToCart}
onBack={() => router.back()}
/>
OrderTrackingScreen
A live order status screen showing a step-by-step timeline from placement to delivery, with estimated arrival and a map preview.
| Prop | Type | Default | Description |
|---|---|---|---|
orderId | string | — | Order reference shown at the top |
status | 'placed' | 'confirmed' | 'preparing' | 'shipped' | 'out_for_delivery' | 'delivered' | — | Current order status |
estimatedDelivery? | string | — | Human-readable ETA label |
steps | TrackingStep[] | — | Each step: { key, label, completedAt? } |
mapRegion? | MapRegion | — | If provided, renders a small map with the delivery pin |
onContactSupport? | () => void | — | Called when “Contact support” is tapped |
onBack? | () => void | — | Back navigation handler |
accessibilityLabel? | string | — | Accessibility label |
import { OrderTrackingScreen } from '@objectifthunes/limestone-sdk';
<OrderTrackingScreen
orderId="#10423"
status="shipped"
estimatedDelivery="Tomorrow, 12 PM – 4 PM"
steps={[
{ key: 'placed', label: 'Order placed', completedAt: '10:00 AM' },
{ key: 'confirmed', label: 'Confirmed', completedAt: '10:05 AM' },
{ key: 'preparing', label: 'Being prepared', completedAt: '11:30 AM' },
{ key: 'shipped', label: 'Shipped' },
{ key: 'delivered', label: 'Delivered' },
]}
onContactSupport={openSupport}
onBack={() => router.back()}
/>
TransactionDetailScreen
A financial transaction detail screen showing amount, direction (credit/debit), merchant info, status, and a line-item breakdown.
| Prop | Type | Default | Description |
|---|---|---|---|
amount | number | — | Transaction amount |
currency? | string | 'USD' | ISO 4217 currency code |
direction | 'credit' | 'debit' | — | Whether the transaction is incoming or outgoing |
merchantName? | string | — | Merchant or counterparty name |
merchantLogo? | string | — | Merchant logo URI |
status | 'pending' | 'completed' | 'failed' | 'refunded' | — | Transaction status |
date? | string | — | Formatted date string |
lineItems? | Array<{ label: string; value: string }> | — | Itemised breakdown |
onBack? | () => void | — | Back navigation handler |
accessibilityLabel? | string | — | Accessibility label |
import { TransactionDetailScreen } from '@objectifthunes/limestone-sdk';
<TransactionDetailScreen
amount={49.99}
currency="USD"
direction="debit"
merchantName="Acme Store"
status="completed"
date="March 29, 2026"
lineItems={[
{ label: 'Subtotal', value: '$44.99' },
{ label: 'Tax', value: '$5.00' },
{ label: 'Total', value: '$49.99' },
]}
onBack={() => router.back()}
/>
CartScreen
A shopping cart screen listing selected items with quantity controls, a price summary, and a checkout button.
| Prop | Type | Default | Description |
|---|---|---|---|
items | CartItem[] | — | Each item: { id, name, imageUri?, price, quantity, variantLabel? } |
onQuantityChange? | (id: string, qty: number) => void | — | Called when the user changes item quantity |
onRemoveItem? | (id: string) => void | — | Called when the user removes an item |
promoCode? | string | — | Applied promo code |
onApplyPromo? | (code: string) => void | — | Called when a promo code is submitted |
subtotal | number | — | Subtotal before tax and shipping |
tax? | number | — | Tax amount |
shipping? | number | — | Shipping cost |
total | number | — | Final total |
currency? | string | 'USD' | ISO 4217 currency code |
onCheckout? | () => void | — | Called when the checkout button is pressed |
accessibilityLabel? | string | — | Accessibility label |
import { CartScreen } from '@objectifthunes/limestone-sdk';
<CartScreen
items={cartItems}
onQuantityChange={(id, qty) => updateQuantity(id, qty)}
onRemoveItem={(id) => removeFromCart(id)}
subtotal={subtotal}
tax={tax}
total={total}
onCheckout={proceedToCheckout}
/>
FeedScreen
A social-style vertical feed with pull-to-refresh, infinite scroll, and a new-posts notification banner. Renders any item type via renderItem.
| Prop | Type | Default | Description |
|---|---|---|---|
items | unknown[] | — | Feed data array |
renderItem | (item: unknown, index: number) => React.ReactNode | — | Row renderer |
keyExtractor | (item: unknown, index: number) => string | — | Unique key per row |
refreshing? | boolean | false | Controls pull-to-refresh spinner |
onRefresh? | () => void | — | Called on pull-to-refresh |
onEndReached? | () => void | — | Called when nearing the end of the list |
newItemCount? | number | 0 | When > 0, shows a “N new posts” tap-to-scroll banner |
onNewItemsBannerPress? | () => void | — | Called when the new-posts banner is tapped |
emptyTitle? | string | 'Nothing here yet' | Empty state heading |
emptyMessage? | string | — | Empty state supporting text |
accessibilityLabel? | string | — | Accessibility label |
import { FeedScreen } from '@objectifthunes/limestone-sdk';
<FeedScreen
items={posts}
keyExtractor={(item) => (item as Post).id}
renderItem={(item) => <PostCard post={item as Post} />}
refreshing={isRefreshing}
onRefresh={reload}
onEndReached={loadMore}
newItemCount={newPosts.length}
onNewItemsBannerPress={scrollToTop}
/>
FormWizardScreen
A multi-step form screen with a step indicator, per-step validation, back/next navigation, and a final submit action.
| Prop | Type | Default | Description |
|---|---|---|---|
steps | WizardStep[] | — | Each step: { key, title, content, validate? } |
currentStep? | number | 0 | Controlled active step index |
onStepChange? | (index: number) => void | — | Called when the step changes |
onSubmit? | () => void | — | Called when the user completes the last step |
nextLabel? | string | 'Next' | Label for the next button |
backLabel? | string | 'Back' | Label for the back button |
submitLabel? | string | 'Submit' | Label for the final submit button |
loading? | boolean | false | Disables navigation and shows loading on submit |
accessibilityLabel? | string | — | Accessibility label |
import { FormWizardScreen } from '@objectifthunes/limestone-sdk';
const steps = [
{ key: 'account', title: 'Create account', content: <AccountStep /> },
{ key: 'profile', title: 'Your profile', content: <ProfileStep /> },
{ key: 'billing', title: 'Billing info', content: <BillingStep /> },
];
<FormWizardScreen
steps={steps}
onSubmit={handleSignup}
loading={isSubmitting}
/>
HistoryScreen
A paginated, filterable history list (transactions, activity log, order history) with date grouping and an optional search bar.
| Prop | Type | Default | Description |
|---|---|---|---|
items | HistoryItem[] | — | Each item: { id, title, subtitle?, amount?, timestamp, icon?, variant? } |
renderItem? | (item: HistoryItem) => React.ReactNode | — | Custom row renderer (overrides default) |
filters? | HistoryFilter[] | — | Each filter: { key, label } — rendered as chips above the list |
activeFilter? | string | — | Key of the currently active filter |
onFilterChange? | (key: string) => void | — | Called when the user taps a filter chip |
showSearch? | boolean | false | Show a search bar above the filter chips |
onSearch? | (query: string) => void | — | Called when the search query changes |
refreshing? | boolean | false | Controls pull-to-refresh spinner |
onRefresh? | () => void | — | Called on pull-to-refresh |
onEndReached? | () => void | — | Called when nearing the end |
emptyTitle? | string | 'No history' | Empty state heading |
accessibilityLabel? | string | — | Accessibility label |
import { HistoryScreen } from '@objectifthunes/limestone-sdk';
<HistoryScreen
items={transactions}
filters={[
{ key: 'all', label: 'All' },
{ key: 'credit', label: 'Credits' },
{ key: 'debit', label: 'Debits' },
]}
activeFilter={filter}
onFilterChange={setFilter}
showSearch
onSearch={searchTransactions}
onEndReached={loadMore}
/>
MediaDetailScreen
A full-screen media viewer for a single image or video with an optional caption, like/share actions, and a comments section.
| Prop | Type | Default | Description |
|---|---|---|---|
mediaUri | string | — | URI of the image or video |
mediaType | 'image' | 'video' | — | Determines which player to render |
caption? | string | — | Text shown below the media |
authorName? | string | — | Uploader name |
authorAvatarUri? | string | — | Uploader avatar URI |
likeCount? | number | — | Like count |
liked? | boolean | false | Whether the current user has liked this item |
onLike? | () => void | — | Called when the like button is pressed |
onShare? | () => void | — | Called when the share button is pressed |
comments? | Comment[] | — | Comments passed to an inline CommentThread |
onAddComment? | (text: string) => void | — | Called when the user submits a comment |
onBack? | () => void | — | Back navigation handler |
accessibilityLabel? | string | — | Accessibility label |
import { MediaDetailScreen } from '@objectifthunes/limestone-sdk';
<MediaDetailScreen
mediaUri="https://example.com/photo.jpg"
mediaType="image"
caption="Sunset over the Alps."
authorName="Alice Martin"
likeCount={142}
liked={hasLiked}
onLike={toggleLike}
onShare={shareMedia}
comments={comments}
onAddComment={postComment}
onBack={() => router.back()}
/>