Component Library Reference

bluesky-social/social-app

Generated by Inkwell Forge — automated codebase documentation analysis. Based on analysis of 131 screens. Subject matter expert review is recommended before distribution.

May 5, 2026

Social App

Component Library Reference (Mobile)

May 2026


Generated by Inkwell Forge — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.


1. Overview

This Component Library Reference covers 131 screens of the social-app React Native application. Screens assessed include: Inbox (/messages/inbox), Shell (/search/shell), Feed (/src/view/com/feeds/feed), Images (/onboarding/step-finished/images), Choose Account Form (/login/choose-account-form), Status Bar Shadow (/profile/header/status-bar-shadow), Shell (/profile/header/shell), Handle Suggestions (/signup/step-handle/handle-suggestions), Miscellaneous Notification Settings, Account Settings, Settings (/messages/settings), Utils (/search/utils), Mention Notification Settings, Settings (/settings), Deactivated (/deactivated), Log (/log), Hashtag (/hashtag), No Saved Feeds Of Any Type, No Following Feed, No Feeds Pinned, Find Contacts Flow, List Hidden, Shared Preferences Tester, Form Container, Set New Password Form, State (/onboarding/state), Login Form, Chat List, Emoji Picker, Value Proposition Pager.shared, Gif, Password Updated Form, Verification Settings, Forgot Password Form, Value Proposition Pager, Expo Scroll Forwarder, Index.web (onboarding/step-find-contacts-intro), Index.web (onboarding/step-find-contacts), Activity List, Layout (/onboarding/layout), Conversation, Avatar Creator Items, Edit Profile Dialog, Growable Banner, Starter Pack Card, Feed Section, Types (/onboarding/step-profile/types), Const (/post-thread/const), Value Proposition Pager.web, Interest Button, Metrics, Handle, Growable Avatar, Avatar Circle, Util (/onboarding/util), Post Liked By, Status Bar Shadow.web, Profile Labeler Liked By, Explore Trending Videos, Explore Trending Topics, Suggested Follows, Profile Followers, Profile Follows, Known Followers, Profile Search, Explore Recommendations, Feed (/profile/sections/feed), Explore Suggested Accounts, Labels (/profile/sections/labels), Search Results, Saved Feeds, Types (/profile/sections/types), Explore Interests Card, Explore (/search/explore), App Passwords, Activity Privacy Settings, External Media Preferences, Index.web (app-icon-settings), Settings List Item.web, App Icon Image, Settings List Item (app-icon-settings), Types (app-icon-settings), Automation Label Settings, Accessibility Settings, Appearance Settings, About Settings, Interests Settings, Content And Media Settings, Find Contacts Settings, Following Feed Preferences, Legacy Notification Settings, Reposts On Reposts Notification Settings, New Follower Notification Settings, Reply Notification Settings, Quote Notification Settings, Privacy And Security Settings, Like Notification Settings, Thread Preferences, Likes On Reposts Notification Settings, Repost Notification Settings, Splash, Starter Pack, Gesture Action, Signup Queued, Draggable Scroll, Takendown, Index.web (video-feed), Error, Back Next Buttons, Step Details, Captcha Web, Types (video-feed), State (/signup/state), State (/starter-pack/wizard/state), Starter Pack Landing, Topic, Step Profiles, Step Feeds, Policies, Captcha Web View.web, Placeholder Canvas, Display Name, Post Quotes, Avatar Creator Circle, Activity Notification Settings, Profile Header Standard, Post Reposted By, Language Settings, Error State, About Section, Profile Header Labeler. Components not referenced in the assessed documentation are outside the scope of this document.

Generated by Inkwell Forge — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.

This reference covers all reusable UI components observable from the assessed screen documentation. The application is built on React Native and Expo, using a custom internal design system (#/alf) for atomic styles and theming. No single third-party UI component library (such as React Native Paper or NativeBase) is used; instead, the application relies on a bespoke component library under #/components/*. Third-party packages include react-native-gesture-handler, react-native-reanimated, expo-image, expo-blur, expo-linear-gradient, react-native-webview, react-native-view-shot, and react-native-keyboard-controller.

Component hierarchy:

React Native vs. web differences: React Native components render to native views, not HTML DOM elements. Viewdiv, Textspan, StyleSheet ≠ CSS classes. All layout uses Flexbox with flexDirection defaulting to 'column'. The application uses .web.tsx / .ios.tsx / .android.tsx file splits for several components (e.g., StatusBarShadow, ValuePropositionPager, CaptchaWebView, ExpoScrollForwarder). These are documented as separate variants of the same logical component.

Platform-specific variants: The following logical components have documented platform splits: StatusBarShadow (iOS animated vs. web null), ValuePropositionPager (native PagerView vs. web single-page), CaptchaWebView (native WebView vs. web iframe), ExpoScrollForwarderView (native passthrough vs. web null), AppIconSettings (native only; web throws), StepFindContactsIntro / StepFindContacts (native only; web throws).

How to use this document: Use the Component Inventory table for quick lookup by component name, then navigate to the full entry using the component ID (C-XX).


2. Component Inventory

# Component Name Category Library Platform Summary Screens Using It
C-01 Layout.Screen Layout Custom iOS + Android Root screen container providing safe area and background All settings, profile, messages, search, onboarding screens
C-02 Layout.Header.Outer Layout Custom iOS + Android Horizontal header bar container All screens with a header
C-03 Layout.Header.BackButton Navigation Custom iOS + Android Back navigation button rendered in the header left slot Most stack screens
C-04 Layout.Header.Content Layout Custom iOS + Android Center content area of the header bar Most screens with a title
C-05 Layout.Header.TitleText Data Display Custom iOS + Android Styled heading text inside the header Most screens
C-06 Layout.Header.SubtitleText Data Display Custom iOS + Android Secondary text below the title in the header Post Liked By, Post Reposted By, Post Quotes, Hashtag
C-07 Layout.Header.Slot Layout Custom iOS + Android Empty slot for layout balance in the header Most screens
C-08 Layout.Header.MenuButton Navigation Custom iOS + Android Hamburger menu button for the left header slot Search Shell, Chat List
C-09 Layout.Content Layout Custom iOS + Android Scrollable content area below the header All settings, profile, auth screens
C-10 Layout.Center Layout Custom iOS + Android Horizontally centered content container with optional side borders Error, No Feeds Pinned, List Hidden, Deactivated
C-11 SafeAreaView (via useSafeAreaInsets) Layout react-native-safe-area-context iOS + Android Provides device safe area inset values StatusBarShadow, GrowableBanner, Signup Queued, Deactivated
C-12 KeyboardAwareScrollView Layout react-native-keyboard-controller iOS + Android Scroll container that adjusts for the on-screen keyboard Takendown, Step Profiles, Step Feeds, Login Form
C-13 StatusBarShadow Layout Custom iOS (animated) / Web (null) Gradient overlay at the top of the screen covering the status bar area Profile Header Shell
C-14 GrowableBanner Layout Custom iOS (animated) / Android (static) Profile banner that scales and blurs on iOS overscroll Profile Header Shell
C-15 GrowableAvatar Layout Custom iOS (animated) / Android (static) Profile avatar that scales on iOS overscroll Profile Header Shell
C-16 FormContainer Layout Custom iOS + Android Login flow step wrapper with title, gap, and responsive gutters Login Form, Forgot Password Form, Set New Password Form, Password Updated Form, Choose Account Form
C-17 List Data Display Custom iOS + Android Virtualized scrollable list wrapping Animated.FlatList Inbox, Chat List, Feed, Hashtag, Profile Followers, Profile Follows, Known Followers, Search Results, Saved Feeds, Activity List, Topic, Step Profiles, Step Feeds, Feed Section, Labels
C-18 ListFooter Data Display Custom iOS + Android Bottom-of-list component showing pagination loading or error Inbox, Chat List, Hashtag, Profile Followers, Profile Follows, Known Followers, Search Results, Activity List, Topic, Activity Notification Settings
C-19 ListMaybePlaceholder Data Display Custom iOS + Android Conditional placeholder showing loading, error, or empty state Hashtag, Profile Followers, Profile Follows, Known Followers, Starter Pack, Starter Pack Landing, Topic
C-20 PostFeed Data Display Custom iOS + Android Animated FlatList of posts with interstitials, pull-to-refresh, and infinite scroll Feed, Feed Section, Activity List
C-21 Post Data Display Custom iOS + Android Individual post card with author, content, embeds, and action controls Feed, Hashtag, Topic, Activity List
C-22 FeedCard.Default Data Display Custom iOS + Android Feed generator card with name, description, and subscribe action Search Results, Explore, Starter Pack Landing, Step Feeds
C-23 ProfileCard.Default Data Display Custom iOS + Android Full profile card with avatar, name, handle, follow button, and description Explore, Starter Pack Landing
C-24 ProfileCard.Avatar Data Display Custom iOS + Android User avatar within a profile card Explore, Activity Notification Settings
C-25 ProfileCard.NameAndHandle Data Display Custom iOS + Android Display name and handle within a profile card Explore, Activity Notification Settings
C-26 ProfileCard.FollowButton Form Custom iOS + Android Follow/unfollow button within a profile card Explore, Activity Notification Settings
C-27 ProfileCard.Description Data Display Custom iOS + Android Bio text within a profile card Explore
C-28 ProfileCard.Link Navigation Custom iOS + Android Navigation link wrapping a profile card Explore, Search Results
C-29 StarterPackCard Data Display Custom iOS + Android Starter pack card with avatar stack, name, description, and follow-all button Onboarding Step Suggested Starterpacks, Explore
C-30 ChatListItem Data Display Custom iOS + Android Conversation row with avatar, name, last message, unread indicator, and swipe actions Inbox, Chat List
C-31 WizardProfileCard Data Display Custom iOS + Android Profile card with checkbox for wizard multi-select Step Profiles
C-32 WizardFeedCard Data Display Custom iOS + Android Feed card with checkbox for wizard multi-select Step Feeds
C-33 RichText Data Display Custom iOS + Android Renders AT Protocol rich text with facets (links, mentions, tags) Feed, Profile Header Standard, Profile Header Labeler, Conversation
C-34 Text (Typography) Data Display Custom iOS + Android Themed text component with emoji support All screens
C-35 EmptyState Data Display Custom iOS + Android Centered icon + message for empty list states Feed Section, No Feeds Pinned, No Saved Feeds, Hashtag, App Passwords, Log
C-36 Admonition Feedback Custom iOS + Android Styled callout box for info, tip, warning, or error messages Settings screens, Notification Settings, Verification Settings, Policies, Explore Interests Card
C-37 CharProgress Data Display Custom iOS + Android Character count progress indicator overlaid on a text field Takendown, Step Details
C-38 AvatarStack Data Display Custom iOS + Android Horizontal stack of overlapping profile avatars Inbox, Chat List, Explore Trending Topics, Starter Pack Card
C-39 ProfileBadges Data Display Custom iOS + Android Inline verification or role badges next to a display name Inbox, Profile Header Standard, Profile Header Labeler
C-40 KnownFollowers Data Display Custom iOS + Android Mutual follower avatars and count shown on a profile or request Inbox, Profile Header Standard
C-41 LoadLatestBtn Navigation Custom iOS + Android Floating button to scroll to top or load new posts Feed, Feed Section, About Section
C-42 Loader Feedback Custom iOS + Android Animated loading spinner icon All screens with loading states
C-43 Skeleton (LoadingPlaceholder) Feedback Custom iOS + Android Shimmer placeholder for loading content Chat List, Explore, Notification Settings
C-44 TextField.Root Form Custom iOS + Android Styled container for a text input field with validation state Login Form, Forgot Password Form, Set New Password Form, Takendown, Step Details, Edit Profile Dialog, App Passwords
C-45 TextField.Input Form Custom iOS + Android The actual text input element within a TextField.Root Login Form, Forgot Password Form, Set New Password Form, Takendown, Step Details, App Passwords
C-46 TextField.LabelText Form Custom iOS + Android Visible label above a text field Login Form, Forgot Password Form, Set New Password Form, Step Details, App Passwords
C-47 TextField.Icon Form Custom iOS + Android Leading icon inside a text field Login Form, Forgot Password Form
C-48 TextField.SuffixText Form Custom iOS + Android Right-aligned suffix text inside a text field (e.g., character counter) Step Details
C-49 Toggle.Group Form Custom iOS + Android Container managing a group of checkbox or radio toggle items Notification Settings screens, Settings (/messages/settings), Appearance Settings, Language Settings, Following Feed Preferences
C-50 Toggle.Item Form Custom iOS + Android Individual selectable item within a Toggle.Group Notification Settings screens, Settings (/messages/settings), Appearance Settings, Language Settings
C-51 Toggle.Platform Form Custom iOS + Android Platform-appropriate toggle control (Switch on native, checkbox on web) Notification Settings screens, Accessibility Settings, External Media Preferences
C-52 Toggle.Radio Form Custom iOS + Android Radio button indicator within a Toggle.Item Notification Settings screens, Settings (/messages/settings)
C-53 Toggle.LabelText Form Custom iOS + Android Styled label text within a Toggle.Item Notification Settings screens, Settings (/messages/settings), Appearance Settings
C-54 Select.Root Form Custom iOS + Android Dropdown/select control for single-value selection Language Settings
C-55 SearchInput Form Custom iOS + Android Search text input with clear button and optional hotkey support Search Shell, Step Profiles, Step Feeds
C-56 SegmentedControl.Root Form Custom iOS + Android Radio-style segmented button group Appearance Settings
C-57 SegmentedControl.Item Form Custom iOS + Android Individual segment within a SegmentedControl.Root Appearance Settings
C-58 Toast Feedback Custom iOS + Android Transient notification message shown at the bottom of the screen Account Settings, Chat List, Inbox, Saved Feeds, Settings, Automation Label Settings
C-59 Prompt.Basic Feedback Custom iOS + Android Simple confirmation dialog with title, description, and confirm/cancel buttons Account Settings, App Passwords, Saved Feeds, List Hidden, Profile Header Standard
C-60 Prompt.Outer Feedback Custom iOS + Android Outer wrapper for a prompt dialog, used with Prompt.Basic Account Settings, Profile Header Labeler
C-61 ErrorScreen Feedback Custom iOS + Android Full-page error display with icon, title, message, optional details, and retry button Error, Find Contacts Flow, App Icon Settings (web), Captcha Web, Starter Pack Landing
C-62 FormError Feedback Custom iOS + Android Inline error message displayed within a form Login Form, Forgot Password Form, Set New Password Form
C-63 TabBar Navigation Custom iOS + Android Horizontal tab label row rendered above a Pager Search Results, Hashtag, Topic, Starter Pack, Profile screens
C-64 Pager Navigation Custom iOS + Android Swipeable horizontal tab container Search Results, Hashtag, Topic, Starter Pack, Profile screens
C-65 PagerWithHeader Navigation Custom iOS + Android Tabbed pager with a sticky header and synchronized scroll Starter Pack, Profile screens
C-66 ProfileMenu Navigation Custom iOS + Android Three-dot overflow menu for profile actions Profile Header Standard, Profile Header Labeler
C-67 OverflowMenu (ConvoMenu) Navigation Custom iOS + Android Context menu for conversation actions (mute, block, report, leave) Inbox, Chat List
C-68 SettingsList.LinkItem Navigation Custom iOS + Android Tappable settings row that navigates to another screen Settings, Account Settings, Content And Media Settings, Privacy And Security Settings, About Settings
C-69 SettingsList.PressableItem Navigation Custom iOS + Android Tappable settings row that triggers an action Settings, Account Settings, About Settings
C-70 GestureActionView Gesture & Touch Custom iOS + Android Horizontal swipe gesture wrapper revealing action buttons at configurable thresholds Inbox, Chat List
C-71 DraggableScrollView Gesture & Touch Custom Web Horizontal ScrollView with click-and-drag scrolling for desktop Explore Trending Videos
C-72 AnimatedPressable Gesture & Touch Custom iOS + Android Pressable with Reanimated-driven opacity/scale animation Starter Pack Landing
C-73 SubtleHover Gesture & Touch Custom iOS + Android Low-opacity hover/press highlight overlay Explore Trending Topics, Explore Suggested Accounts
C-74 BlockDrawerGesture Gesture & Touch Custom iOS + Android Wrapper that prevents the navigation drawer from intercepting horizontal swipes Explore Trending Videos, Explore Suggested Accounts
C-75 Dialog.Outer Utility Custom iOS + Android Root dialog container with native/web presentation options Account Settings, App Passwords, Edit Profile Dialog, Conversation
C-76 Dialog.ScrollableInner Utility Custom iOS + Android Scrollable content area within a dialog with header slot Account Settings, App Passwords, Edit Profile Dialog
C-77 Dialog.Input Utility Custom iOS + Android Styled text input for use inside a dialog Account Settings, App Passwords
C-78 Portal / createPortalGroup Utility Custom iOS + Android Teleports child content to a different position in the component tree Onboarding Layout, SettingsList.Group
C-79 ScreenTransition Utility Custom iOS + Android Directional slide/fade animation wrapper for wizard step transitions Find Contacts Flow, Starter Pack Wizard steps
C-80 GifView Utility expo-bluesky-gif-view (Custom Expo Module) iOS + Android Native GIF playback component with play/pause/toggle control Conversation (inferred from module documentation)
C-81 EmojiPicker Utility expo-emoji-picker (Custom Expo Module) iOS + Android Native emoji picker view that fires onEmojiSelected Conversation (inferred from module documentation)
C-82 ExpoScrollForwarderView Utility expo-scroll-forwarder (Custom Expo Module) iOS + Android (native passthrough) / Web (null) Forwards scroll/gesture events from a non-scrollable area to a target ScrollView Profile screens (inferred from module documentation)
C-83 AgeRestrictedScreen Utility Custom iOS + Android Wrapper that gates content behind age assurance, showing a restricted notice if not met Inbox, Chat List, Conversation
C-84 SettingsList.Container Utility Custom iOS + Android Full-width vertical list wrapper with vertical padding All settings screens
C-85 SettingsList.Group Utility Custom iOS + Android Grouped settings section using Portal to render icon/title in a header row Settings screens with grouped items
C-86 SettingsList.Item Utility Custom iOS + Android Horizontal row layout for a single settings entry All settings screens
C-87 SettingsList.Divider Utility Custom iOS + Android Horizontal rule separator between settings groups All settings screens
C-88 PreferenceControls Utility Custom iOS + Android Reusable toggle controls for notification channel and filter preferences All notification settings screens
C-89 ItemTextWithSubtitle Utility Custom iOS + Android Title + subtitle text block used within a settings item Notification Settings screens
C-90 InlineLinkText Utility Custom iOS + Android Tappable hyperlink rendered inline within a text block No Following Feed, No Feeds Pinned, Policies, Verification Settings, Privacy And Security Settings, Language Settings
C-91 Link Navigation Custom iOS + Android Navigation link component rendering as anchor on web and using React Navigation on native Feed, Search, Profile, Starter Pack, Hashtag
C-92 Button Gesture & Touch Custom iOS + Android Pressable button with variant, color, size, shape, and label props All interactive screens
C-93 ButtonIcon Utility Custom iOS + Android Icon slot within a Button component All screens with icon buttons
C-94 ButtonText Utility Custom iOS + Android Text slot within a Button component All screens with text buttons
C-95 Logo Data Display Custom iOS + Android Bluesky icon mark SVG component Splash, Signup Queued, Deactivated, Onboarding Layout, Starter Pack Landing
C-96 Logotype Data Display Custom iOS + Android Bluesky wordmark SVG component Splash
C-97 UserAvatar Data Display Custom iOS + Android Profile picture with moderation, live status, and type (circle/rect) support Inbox, Chat List, Settings, Profile Header Shell, Onboarding
C-98 UserBanner Data Display Custom iOS + Android Profile banner image with moderation and type (user/labeler) support Profile Header Shell, Edit Profile Dialog
C-99 EditableUserAvatar Data Display Custom iOS + Android Avatar with an edit affordance for profile editing Edit Profile Dialog
C-100 LiveIndicator Data Display Custom iOS + Android Animated live status indicator overlaid on an avatar Profile Header Shell, Profile Header Standard, Profile Header Labeler

3. Layout Components

C-01: Layout.Screen

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Root screen container that provides safe area handling, background color, and a consistent full-screen wrapper for all screens in the application.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
testID string No Test identifier for automated testing and accessibility tooling
style StyleProp<ViewStyle> No Additional styles applied to the root container
children React.ReactNode Yes Screen content

Platform-Specific Behavior:

Behavior iOS Android Notes
Safe area Respects notch, Dynamic Island, home indicator Respects status bar and navigation bar Uses react-native-safe-area-context internally

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Full-screen container with theme background color

Composition:

<Layout.Screen testID="myScreen">
  <Layout.Header.Outer>
    <Layout.Header.BackButton />
    <Layout.Header.Content>
      <Layout.Header.TitleText>My Screen</Layout.Header.TitleText>
    </Layout.Header.Content>
    <Layout.Header.Slot />
  </Layout.Header.Outer>
  <Layout.Content>
    {/* screen body */}
  </Layout.Content>
</Layout.Screen>

Accessibility:

Related Components:


C-02: Layout.Header.Outer

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Horizontal header bar container that wraps the top navigation area of a screen, providing consistent height, border, and background styling.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
noBottomBorder boolean No false Suppresses the bottom border of the header
children React.ReactNode Yes Header content (back button, title, slot)
style StyleProp<ViewStyle> No Additional styles

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Full-width bar with bottom border
No border noBottomBorder={true} Bottom border is hidden

Composition:

<Layout.Header.Outer>
  <Layout.Header.BackButton />
  <Layout.Header.Content>
    <Layout.Header.TitleText>Title</Layout.Header.TitleText>
  </Layout.Header.Content>
  <Layout.Header.Slot />
</Layout.Header.Outer>

Accessibility:

Related Components:


C-03: Layout.Header.BackButton

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Back navigation button rendered in the left slot of the header bar, using React Navigation's goBack() behavior.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
onPress () => void No navigation.goBack() Custom press handler; defaults to stack back navigation

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Left-pointing chevron or arrow icon

Composition:

<Layout.Header.Outer>
  <Layout.Header.BackButton />
  {/* ... */}
</Layout.Header.Outer>

Accessibility:

Related Components:


C-04: Layout.Header.Content

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Center content area of the header bar, typically containing the title and optional subtitle.

When to Use:

Props:

Prop Type Required Default Description
align 'center' | 'left' | 'platform' No 'platform' Text alignment of the header content
children React.ReactNode Yes Title and subtitle content

Variants / Visual States:

State/Variant Trigger Visual Behavior
Center aligned align="center" Title centered in the header
Left aligned align="left" Title left-aligned
Platform default align="platform" Center on mobile, left on tablet

Composition:

<Layout.Header.Content align="center">
  <Layout.Header.TitleText>Screen Title</Layout.Header.TitleText>
  <Layout.Header.SubtitleText>42 items</Layout.Header.SubtitleText>
</Layout.Header.Content>

Accessibility:

Related Components:


C-05: Layout.Header.TitleText

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Styled heading text displayed in the center of the header bar, representing the screen's primary title.

When to Use:

Props:

Prop Type Required Default Description
children React.ReactNode Yes The title string or translated content

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Bold, appropriately sized heading text

Composition:

<Layout.Header.Content>
  <Layout.Header.TitleText>
    <Trans>Notifications</Trans>
  </Layout.Header.TitleText>
</Layout.Header.Content>

Accessibility:

Related Components:


C-06: Layout.Header.SubtitleText

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Secondary text displayed below the title in the header, typically showing a count or contextual detail.

When to Use:

Props:

Prop Type Required Default Description
children React.ReactNode Yes The subtitle string or translated content

Composition:

<Layout.Header.Content>
  <Layout.Header.TitleText>Liked By</Layout.Header.TitleText>
  <Layout.Header.SubtitleText>
    <Plural value={likeCount ?? 0} one="# like" other="# likes" />
  </Layout.Header.SubtitleText>
</Layout.Header.Content>

Accessibility:

Related Components:


C-07: Layout.Header.Slot

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Empty slot in the header used to balance the layout when only one side has a button, or to host a right-side action button.

When to Use:

Props:

Prop Type Required Default Description
children React.ReactNode No Optional content to render in the slot

Composition:

<Layout.Header.Outer>
  <Layout.Header.BackButton />
  <Layout.Header.Content>
    <Layout.Header.TitleText>Title</Layout.Header.TitleText>
  </Layout.Header.Content>
  <Layout.Header.Slot>
    {/* optional right-side button */}
  </Layout.Header.Slot>
</Layout.Header.Outer>

Accessibility:

Related Components:


C-08: Layout.Header.MenuButton

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Hamburger menu button rendered in the left slot of the header, used on root screens to open the navigation drawer.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
onPress () => void No Custom press handler; defaults to opening the navigation drawer

Composition:

<Layout.Header.Outer>
  <Layout.Header.MenuButton />
  <Layout.Header.Content>
    <Layout.Header.TitleText>Chats</Layout.Header.TitleText>
  </Layout.Header.Content>
  <Layout.Header.Slot />
</Layout.Header.Outer>

Accessibility:

Related Components:


C-09: Layout.Content

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Scrollable content area below the header, providing consistent padding and background for screen body content.

When to Use:

Props:

Prop Type Required Default Description
scrollEnabled boolean No true Whether the content area is scrollable
ignoreTabletLayoutOffset boolean No false Suppresses tablet-specific layout offset
children React.ReactNode Yes Screen body content
style StyleProp<ViewStyle> No Additional styles

Composition:

<Layout.Screen>
  <Layout.Header.Outer>{/* ... */}</Layout.Header.Outer>
  <Layout.Content>
    <SettingsList.Container>
      {/* settings items */}
    </SettingsList.Container>
  </Layout.Content>
</Layout.Screen>

Accessibility:

Related Components:


C-10: Layout.Center

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Horizontally centered content container with optional side borders, used for error states, empty states, and narrow-column content.

When to Use:

Props:

Prop Type Required Default Description
sideBorders boolean No false Renders vertical border lines on the left and right sides
children React.ReactNode Yes Content to center
style StyleProp<ViewStyle> No Additional styles

Composition:

<Layout.Center sideBorders>
  <EmptyState
    icon={<MessageIcon />}
    message="No messages yet"
  />
</Layout.Center>

Accessibility:

Related Components:


C-11: SafeAreaView (via useSafeAreaInsets)

Category: Layout

Library: react-native-safe-area-context

Platform: iOS + Android

Summary: Provides device-specific safe area inset values (notch, home indicator, status bar) so UI elements can be positioned correctly on all device form factors.

When to Use:

When NOT to Use:

Props:

The hook useSafeAreaInsets() returns { top, bottom, left, right } in logical pixels.

Platform-Specific Behavior:

Behavior iOS Android Notes
Top inset Notch / Dynamic Island height Status bar height Varies by device
Bottom inset Home indicator height Navigation bar height 0 on devices without gesture navigation

Composition:

const insets = useSafeAreaInsets()

<View style={{ paddingBottom: insets.bottom + 16 }}>
  {/* fixed bottom content */}
</View>

Accessibility:

Related Components:


C-12: KeyboardAwareScrollView

Category: Layout

Library: react-native-keyboard-controller

Platform: iOS + Android

Summary: Scroll container that automatically adjusts its content offset when the software keyboard appears, keeping focused inputs visible.

When to Use:

Props:

Prop Type Required Default Description
children React.ReactNode Yes Form content
style StyleProp<ViewStyle> No Additional styles
contentContainerStyle StyleProp<ViewStyle> No Styles for the scroll content container

Platform-Specific Behavior:

Behavior iOS Android Notes
Keyboard avoidance Scrolls content up Scrolls content up Both platforms supported by react-native-keyboard-controller

Composition:

<KeyboardAwareScrollView>
  <TextField.Root>
    <TextField.Input label="Email" keyboardType="email-address" />
  </TextField.Root>
  <TextField.Root>
    <TextField.Input label="Password" secureTextEntry />
  </TextField.Root>
</KeyboardAwareScrollView>

Accessibility:

Related Components:


C-13: StatusBarShadow

Category: Layout

Library: Custom

Platform: iOS (animated) / Web (null render)

Summary: Gradient overlay anchored to the top of the screen that visually separates the status bar from the profile banner content beneath it. On iOS with pager context, it animates with scroll position.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
topInset number Yes Height of the gradient, matching the device's safe area top inset

Platform-Specific Behavior:

Behavior iOS Android Notes
Animation Translates upward on overscroll via Reanimated worklet Static gradient Driven by scrollY shared value from PagerHeaderContext
Web Renders null .web.tsx variant returns null

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default No scroll Black-to-transparent gradient at top of screen
Overscroll (iOS) User pulls down past top Gradient slides upward off-screen

Composition:

<StatusBarShadow topInset={topInset} />

Accessibility:

Known Issues:

Related Components:


C-14: GrowableBanner

Category: Layout

Library: Custom

Platform: iOS (animated) / Android (static)

Summary: Profile banner image container that scales up and applies a blur effect on iOS overscroll, creating a parallax/rubber-band effect. On Android and web, renders a static banner.

When to Use:

Props:

Prop Type Required Default Description
onPress () => void | undefined No Handler for tapping the banner; opens lightbox when provided
backButton React.ReactNode No Back button node rendered over the banner
isPlaceholderProfile boolean No false Shows a loading placeholder instead of the banner image
isFetching boolean No false Shows a pull-to-refresh spinner when overscrolled
bannerRef AnimatedRef<Animated.View> No Ref for measuring the banner's screen position
children React.ReactNode Yes The banner image content

Platform-Specific Behavior:

Behavior iOS Android Notes
Scale animation Scales up from bottom on overscroll No animation Driven by scrollY shared value
Blur effect AnimatedBlurView intensifies on overscroll No blur expo-blur used on iOS only
Pull-to-refresh spinner ActivityIndicator shown when overscrolled + fetching Not shown useStickyToggle prevents flicker

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Normal scroll Static banner image
Overscroll (iOS) User pulls down Banner scales up, blur intensifies
Loading isPlaceholderProfile={true} LoadingPlaceholder shimmer
Fetching isFetching={true} + overscrolled White ActivityIndicator spinner

Composition:

<GrowableBanner
  onPress={onPressBanner}
  backButton={<BackButton />}
  bannerRef={bannerRef}
  isPlaceholderProfile={isPlaceholderProfile}
>
  <UserBanner profile={profile} moderation={moderation} />
</GrowableBanner>

Accessibility:

Known Issues:

Related Components:


C-15: GrowableAvatar

Category: Layout

Library: Custom

Platform: iOS (animated) / Android (static)

Summary: Profile avatar container that scales up from its bottom-left corner on iOS overscroll. On Android and web, renders a static wrapper.

When to Use:

Props:

Prop Type Required Default Description
style StyleProp<ViewStyle> No Additional styles applied to the container
children React.ReactNode Yes The avatar image content

Platform-Specific Behavior:

Behavior iOS Android Notes
Scale animation Scales up from bottom-left on overscroll No animation transformOrigin: 'bottom left', driven by scrollY
Extrapolation Clamped at right (no shrink on downward scroll) Left extrapolation not clamped (potential scale > 1.2)

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Normal scroll Static avatar at normal size
Overscroll (iOS) User pulls down Avatar scales up to 1.2×

Composition:

<GrowableAvatar style={styles.avatar}>
  <UserAvatar profile={profile} size={90} />
</GrowableAvatar>

Accessibility:

Known Issues:

Related Components:


C-16: FormContainer

Category: Layout

Library: Custom

Platform: iOS + Android

Summary: Login flow step wrapper that provides a consistent vertical layout with a title (mobile only), gap spacing, and responsive horizontal gutters.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
titleText string No Title displayed at the top on mobile viewports only
testID string No Test identifier
style StyleProp<ViewStyle> No Additional styles
children React.ReactNode Yes Form content

Variants / Visual States:

State/Variant Trigger Visual Behavior
Mobile !gtMobile Title rendered, horizontal gutters applied
Desktop gtMobile Title suppressed, gutters not applied

Composition:

<FormContainer titleText="Sign in" testID="loginForm">
  <HostingProvider />
  <TextField.Root>
    <TextField.Input label="Username" />
  </TextField.Root>
  <Button label="Sign in" onPress={onPressNext}>
    <ButtonText>Sign in</ButtonText>
  </Button>
</FormContainer>

Accessibility:

Related Components:


4. Data Display Components

C-17: List

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Virtualized scrollable list wrapping React Native's Animated.FlatList, with built-in pull-to-refresh, infinite scroll, and performance tuning props.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
data readonly T[] Yes Array of items to render
renderItem ListRenderItem<T> Yes Function that renders each item
keyExtractor (item: T, index: number) => string Yes Unique key for each item
onRefresh () => void No Pull-to-refresh callback
refreshing boolean No false Whether pull-to-refresh is active
onEndReached () => void No Callback when scroll reaches near the end
onEndReachedThreshold number No Fraction of list height from end to trigger onEndReached
ListHeaderComponent React.ComponentType | React.ReactElement No Component rendered above the list
ListFooterComponent React.ComponentType | React.ReactElement No Component rendered below the list
ListEmptyComponent React.ComponentType | React.ReactElement No Component rendered when data is empty
windowSize number No Number of viewport-heights of items to keep rendered
initialNumToRender number No Number of items to render on initial mount
desktopFixedHeight boolean No Web-only: enables fixed-height scrollable container
sideBorders boolean No Renders side border lines
style StyleProp<ViewStyle> No Additional styles
contentContainerStyle StyleProp<ViewStyle> No Styles for the scroll content container

Platform-Specific Behavior:

Behavior iOS Android Notes
maxToRenderPerBatch 20 (inferred) 10 (inferred) Tuned per platform in PostFeed
removeClippedSubviews true true Removes off-screen views from native hierarchy

Variants / Visual States:

State/Variant Trigger Visual Behavior
Loading data is empty, ListEmptyComponent shows spinner Spinner or skeleton
Empty data is empty, no loading Empty state message
Populated data has items Virtualized list of items
Refreshing refreshing={true} Native pull-to-refresh spinner

Composition:

<List
  data={conversations}
  renderItem={({ item }) => <ChatListItem convo={item} />}
  keyExtractor={item => item.id}
  onRefresh={onRefresh}
  refreshing={isPTRing}
  onEndReached={onEndReached}
  onEndReachedThreshold={1.5}
  ListFooterComponent={<ListFooter isFetchingNextPage={isFetchingNextPage} />}
  windowSize={11}
/>

Accessibility:

Related Components:


C-18: ListFooter

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Bottom-of-list component that shows a loading spinner during pagination or an error message with a retry button when a page fetch fails.

When to Use:

Props:

Prop Type Required Default Description
isFetchingNextPage boolean No false Shows a loading spinner when true
error string No Error message to display with a retry button
onRetry () => void No Callback for the retry button
hasNextPage boolean No Whether more pages are available
style StyleProp<ViewStyle> No Additional styles

Variants / Visual States:

State/Variant Trigger Visual Behavior
Loading isFetchingNextPage={true} Centered ActivityIndicator spinner
Error error is truthy Error message text with a retry button
End of list !hasNextPage Transparent border spacer
Default No special state Transparent spacer

Composition:

<List
  data={items}
  renderItem={renderItem}
  keyExtractor={keyExtractor}
  ListFooterComponent={
    <ListFooter
      isFetchingNextPage={isFetchingNextPage}
      error={cleanError(error)}
      onRetry={fetchNextPage}
    />
  }
/>

Accessibility:

Related Components:


C-19: ListMaybePlaceholder

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Conditional placeholder component that renders a loading spinner, error state with retry, or empty state message depending on the provided props.

When to Use:

Props:

Prop Type Required Default Description
isLoading boolean No false Shows a loading spinner
isError boolean No false Shows an error state
error string No Error message to display
emptyType 'results' | 'feed' No Type of empty state message
emptyMessage string No Custom empty state message
onRetry () => void No Retry callback for error state

Variants / Visual States:

State/Variant Trigger Visual Behavior
Loading isLoading={true} Centered spinner
Error isError={true} Error message with retry button
Empty (results) emptyType="results" "No results" message
Empty (feed) emptyType="feed" Feed-specific empty message

Composition:

{posts.length < 1 ? (
  <ListMaybePlaceholder
    isLoading={isLoading}
    isError={isError}
    error={cleanError(error)}
    emptyType="results"
    emptyMessage="We couldn't find any results for that topic."
    onRetry={refetch}
  />
) : (
  <List data={posts} renderItem={renderItem} keyExtractor={keyExtractor} />
)}

Accessibility:

Related Components:


C-20: PostFeed

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Animated FlatList of posts with heterogeneous row types (posts, interstitials, loading placeholders, error rows), pull-to-refresh, infinite scroll, and feed polling.

When to Use:

When NOT to Use:

Props:

Prop Type Required Default Description
feed FeedDescriptor Yes Identifies which feed to load
feedParams FeedParams No Optional parameters for the feed query
renderEmptyState () => JSX.Element No Caller-provided empty state renderer
renderEndOfFeed () => JSX.Element No Caller-provided end-of-feed renderer
onHasNew (v: boolean) => void No Callback when new posts are detected
onScrolledDownChange (v: boolean) => void No Callback when scroll position changes
scrollElRef ListRef No Ref for programmatic scroll control
headerOffset number No 0 Offset for sticky header height
isPageFocused boolean No true Whether this feed page is currently visible
isPageAdjacent boolean No false Whether this feed page is adjacent to the focused page (enables prefetch)
disablePoll boolean No false Disables background polling for new posts
enabled boolean No true Whether the feed query is enabled

Platform-Specific Behavior:

Behavior iOS Android Notes
maxToRenderPerBatch 20 1 Tuned per platform
onEndReachedThreshold 2 2 Triggers 2 viewport-heights before end
Scroll animation Animated Animated Uses react-native-reanimated

Variants / Visual States:

State/Variant Trigger Visual Behavior
Loading Initial fetch PostFeedLoadingPlaceholder skeleton rows
Error API failure PostFeedErrorMessage with retry
Empty No posts Caller-provided renderEmptyState()
Populated Posts available Virtualized list of PostFeedItem rows
Load more error Pagination failure LoadMoreRetryBtn at bottom

Composition:

<PostFeed
  feed="following"
  onHasNew={setHasNew}
  onScrolledDownChange={setIsScrolledDown}
  scrollElRef={scrollElRef}
  renderEmptyState={() => <EmptyState message="No posts yet" />}
  isPageFocused={isPageFocused}
/>

Accessibility:

Related Components:


C-21: Post

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Individual post card rendering author information, rich text content, embedded media, and interaction controls (like, repost, reply).

When to Use:

Props:

Prop Type Required Default Description
post AppBskyFeedDefs.PostView Yes The post data to render
record AppBskyFeedPost.Record Yes The post record content
moderation ModerationDecision Yes Moderation decision for this post
onBeforePress () => void No Callback fired before navigating to the post thread
showReplyTo boolean No false Whether to show the reply-to indicator
isThreadChild boolean No false Whether this post is a child in a thread
isThreadParent boolean No false Whether this post is a parent in a thread

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Normal post Full post card with author, content, controls
Moderated Moderation decision Content may be blurred or hidden
Thread child isThreadChild={true} Indented with connecting line

Composition:

<List
  data={posts}
  renderItem={({ item }) => (
    <Post
      post={item.post}
      record={item.record}
      moderation={item.moderation}
      onBeforePress={onBeforePress}
    />
  )}
  keyExtractor={item => item.post.uri}
/>

Accessibility:

Related Components:


C-22: FeedCard.Default

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Feed generator card displaying the feed's name, description, avatar, and a subscribe/save action.

When to Use:

Props:

Prop Type Required Default Description
view AppBskyFeedDefs.GeneratorView Yes The feed generator data
showSaveBtn boolean No false Whether to show a save/pin button
showDescription boolean No true Whether to show the feed description
showLikes boolean No false Whether to show the like count
style StyleProp<ViewStyle> No Additional styles

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Feed avatar, name, description
With save button showSaveBtn={true} Save/pin button visible

Composition:

<FeedCard.Default
  view={feedGeneratorView}
  showSaveBtn
  showDescription
/>

Accessibility:

Related Components:


C-23: ProfileCard.Default

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Full profile card with avatar, display name, handle, follow button, and bio description, used in explore and suggestion surfaces.

When to Use:

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes The profile data
moderationOpts ModerationOpts Yes Moderation options for content filtering
logContext string No Analytics context string
recId string No Recommendation ID for analytics
sourceContext object No Source context for analytics attribution

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Avatar, name, handle, follow button, description
Moderated Moderation decision Content may be blurred

Composition:

<ProfileCard.Default
  profile={actor}
  moderationOpts={moderationOpts}
  logContext="Explore"
  recId={actor.recId}
/>

Accessibility:

Related Components:


C-24: ProfileCard.Avatar

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: User avatar sub-component within a profile card, with moderation applied.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes Profile data
moderationOpts ModerationOpts Yes Moderation options
size number No 42 Avatar size in logical pixels

Composition:

<ProfileCard.Outer>
  <ProfileCard.Header>
    <ProfileCard.Avatar profile={profile} moderationOpts={moderationOpts} />
    <ProfileCard.NameAndHandle profile={profile} />
  </ProfileCard.Header>
</ProfileCard.Outer>

Related Components:


C-25: ProfileCard.NameAndHandle

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Display name and @handle sub-component within a profile card.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes Profile data
moderationOpts ModerationOpts Yes Moderation options

Related Components:


C-26: ProfileCard.FollowButton

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Follow/unfollow button sub-component within a profile card, with analytics tracking.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes Profile data
logContext string No Analytics context
withIcon boolean No true Whether to show an icon inside the button
onFollow () => void No Callback fired after following

Related Components:


C-27: ProfileCard.Description

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Bio/description text sub-component within a profile card, clamped to 2 lines.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes Profile data
numberOfLines number No 2 Maximum number of lines to display

Related Components:


Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Navigation link wrapper that makes an entire profile card tappable, navigating to the profile screen.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes Profile data used to construct the navigation target
label string No Accessible label for the link
children React.ReactNode Yes Card content

Related Components:


C-29: StarterPackCard

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Starter pack card displaying an avatar stack of members, the pack name, description, and a "Follow all" button with analytics tracking.

When to Use:

Props:

Prop Type Required Default Description
view AppBskyGraphDefs.StarterPackView Yes The starter pack data

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Avatar stack, name, description, "Follow all" button
Processing Follow-all in progress Loader icon in button, button disabled
All followed Follow-all succeeded Checkmark icon in button, button disabled

Composition:

<StarterPackCard view={starterPackView} />

Accessibility:

Known Issues:

Related Components:


C-30: ChatListItem

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Conversation row displaying the other user's avatar, display name, last message preview, elapsed time, unread indicator, and swipe gesture actions.

When to Use:

Props:

Prop Type Required Default Description
convo ChatBskyConvoDefs.ConvoView Yes The conversation data
onPress () => void No Custom press handler
showMenu boolean No false Whether to show the ConvoMenu

Platform-Specific Behavior:

Behavior iOS Android Notes
Long press Opens ConvoMenu Opens ConvoMenu Plays haptic on native
Swipe gestures GestureActionView GestureActionView Mark as read / delete
Hover/focus Shows ConvoMenu trigger Web only

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Normal conversation Avatar, name, last message, time
Unread convo.unreadCount > 0 Bold last message, blue unread dot
Deleted account handle === 'missing.invalid' "Deleted Account" name, muted styling
Muted/blocked convo.muted or blocked Dimmed styling, muted bell icon

Composition:

<List
  data={conversations}
  renderItem={({ item }) => <ChatListItem convo={item} />}
  keyExtractor={item => item.id}
/>

Accessibility:

Known Issues:

Related Components:


C-31: WizardProfileCard

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Profile card with a checkbox interaction model for multi-select in the Starter Pack wizard's profile selection step.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic Yes Profile data
moderationOpts ModerationOpts Yes Moderation options
state WizardState Yes Wizard state for determining checked status
dispatch WizardDispatch Yes Wizard dispatch for toggling selection

Related Components:


C-32: WizardFeedCard

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Feed card with a checkbox interaction model for multi-select in the Starter Pack wizard's feed selection step.

Props:

Prop Type Required Default Description
feed AppBskyFeedDefs.GeneratorView Yes Feed generator data
state WizardState Yes Wizard state for determining checked status
dispatch WizardDispatch Yes Wizard dispatch for toggling selection

Related Components:


C-33: RichText

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Renders AT Protocol rich text with inline facets (links, mentions, hashtags) as interactive inline elements.

When to Use:

Props:

Prop Type Required Default Description
value RichTextAPI Yes Pre-parsed AT Protocol rich text object
numberOfLines number No Maximum number of lines to display
style StyleProp<TextStyle> No Additional text styles
enableTags boolean No false Whether to render hashtag facets as links
authorHandle string No Author handle for tag link construction
onLinkPress (url: string) => void No Custom handler for link presses

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Plain text Unstyled text
With links Facets present Inline tappable links in primary color
Truncated numberOfLines set Text truncated with ellipsis

Composition:

<RichText
  value={descriptionRT}
  numberOfLines={15}
  enableTags
  authorHandle={profile.handle}
/>

Accessibility:

Related Components:


C-34: Text (Typography)

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Themed text component with emoji support, used throughout the application for all text rendering.

When to Use:

Props:

Prop Type Required Default Description
style StyleProp<TextStyle> No Additional text styles
emoji boolean No false Enables emoji rendering support
numberOfLines number No Maximum number of lines
selectable boolean No false Whether text is selectable
children React.ReactNode Yes Text content

Composition:

<Text style={[a.text_xl, a.font_bold, t.atoms.text]}>
  Display Name
</Text>

Accessibility:

Related Components:


C-35: EmptyState

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Centered icon and message component displayed when a list or feed has no content to show.

When to Use:

Props:

Prop Type Required Default Description
icon React.ReactNode No Icon to display above the message
message string Yes Human-readable empty state message
button EmptyStateButtonProps No Optional call-to-action button

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default No items Icon + message centered
With button button prop provided Icon + message + CTA button

Composition:

<PostFeed
  feed={feed}
  renderEmptyState={() => (
    <EmptyState
      icon={<EditIcon size="3xl" />}
      message="No posts yet"
    />
  )}
/>

Accessibility:

Related Components:


C-36: Admonition

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Styled callout box for displaying informational, tip, warning, or error messages to the user.

When to Use:

Props:

Prop Type Required Default Description
type 'info' | 'tip' | 'warning' | 'error' Yes Visual style of the callout
children React.ReactNode Yes Callout content
style StyleProp<ViewStyle> No Additional styles

Variants / Visual States:

State/Variant Trigger Visual Behavior
Info type="info" Blue-tinted informational box
Tip type="tip" Neutral tip box
Warning type="warning" Yellow-tinted warning box
Error type="error" Red-tinted error box

Composition:

{isError && (
  <Admonition type="error">
    <Trans>Failed to load notification settings.</Trans>
  </Admonition>
)}

Accessibility:

Related Components:


C-37: CharProgress

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Character count progress indicator displayed as {n}/50 overlaid on a text field, showing how many characters have been used.

When to Use:

Props:

Prop Type Required Default Description
count number Yes Current character count
max number Yes Maximum allowed character count

Composition:

<View style={a.relative}>
  <TextField.Root>
    <TextField.Input
      value={name}
      onChangeText={text => dispatch({ type: 'SetName', name: text })}
    />
  </TextField.Root>
  <CharProgress count={state.name?.length ?? 0} max={50} />
</View>

Accessibility:

Related Components:


C-38: AvatarStack

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Horizontal stack of overlapping circular profile avatars, used to preview a group of users.

When to Use:

Props:

Prop Type Required Default Description
profiles AppBskyActorDefs.ProfileViewBasic[] Yes Array of profiles to display
numPending number No Number of profiles to show
total number No Total count for overflow display
size number No 20 Avatar size in logical pixels

Composition:

<AvatarStack
  profiles={inboxUnreadConvoMembers.slice(0, 3)}
  numPending={3}
  total={inboxUnreadConvoMembers.length}
/>

Accessibility:

Related Components:


C-39: ProfileBadges

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Inline verification or role badges rendered next to a user's display name.

When to Use:

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewDetailed Yes Profile data
size 'sm' | 'md' | 'lg' | 'xl' No 'md' Badge size
interactive boolean No false Whether badges are tappable

Composition:

<View style={a.flex_row}>
  <Text style={a.font_bold}>{displayName}</Text>
  <ProfileBadges profile={profile} size="xl" interactive />
</View>

Accessibility:

Related Components:


C-40: KnownFollowers

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Displays mutual follower avatars and a count, providing social context when viewing a profile or chat request.

When to Use:

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewDetailed Yes Profile data containing knownFollowers
moderationOpts ModerationOpts Yes Moderation options

Composition:

<KnownFollowers
  profile={otherUser}
  moderationOpts={moderationOpts}
/>

Related Components:


C-41: LoadLatestBtn

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Floating circular button that appears when the user has scrolled down or new posts are available, allowing one-tap return to the top of a feed.

When to Use:

Props:

Prop Type Required Default Description
onPress () => void Yes Callback to scroll to top and load new posts
label string Yes Accessible label for the button
showIndicator boolean No false Whether to show a dot indicating new content

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default isScrolledDown or hasNew Circular button visible
With indicator showIndicator={true} Blue dot on the button

Composition:

{(isScrolledDown || hasNew) && (
  <LoadLatestBtn
    onPress={onScrollToTop}
    label={_(msg`Load new posts`)}
    showIndicator={hasNew}
  />
)}

Accessibility:

Related Components:


C-42: Loader

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Animated loading spinner icon, used both as a standalone centered spinner and as an icon inside buttons during async operations.

When to Use:

Props:

Prop Type Required Default Description
size 'sm' | 'md' | 'lg' | 'xl' No 'md' Spinner size
style StyleProp<ViewStyle> No Additional styles

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Rendered Animated spinning indicator

Composition:

{/* As a section loading indicator */}
{isPending && <Loader size="xl" />}

{/* Inside a button */}
<Button label="Save" onPress={onSave} disabled={isPending}>
  {isPending && <ButtonIcon icon={Loader} />}
  <ButtonText>Save</ButtonText>
</Button>

Accessibility:

Related Components:


C-43: Skeleton (LoadingPlaceholder)

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Shimmer placeholder component that mimics the shape of content while it is loading, reducing perceived load time.

When to Use:

Props:

Prop Type Required Default Description
width number | string No Width of the placeholder
height number No Height of the placeholder
style StyleProp<ViewStyle> No Additional styles

Composition:

{isLoading ? (
  <View style={a.flex_row}>
    <Skeleton width={40} height={40} style={a.rounded_full} />
    <Skeleton width={120} height={16} style={a.ml_sm} />
  </View>
) : (
  <ProfileCard.Default profile={profile} />
)}

Accessibility:

Related Components:


5. Form Components

C-44: TextField.Root

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Styled container for a text input field that provides border, background, and validation state styling.

When to Use:

Props:

Prop Type Required Default Description
isInvalid boolean No false Applies invalid/error styling to the field border
style StyleProp<ViewStyle> No Additional styles
children React.ReactNode Yes TextField.Icon, TextField.Input, TextField.SuffixText

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Neutral border and background
Invalid isInvalid={true} Red/error-colored border

Composition:

<TextField.LabelText>Email</TextField.LabelText>
<TextField.Root isInvalid={errorField === 'identifier'}>
  <TextField.Icon icon={At} />
  <TextField.Input
    label="Enter your email"
    keyboardType="email-address"
    autoCapitalize="none"
    value={email}
    onChangeText={setEmail}
  />
</TextField.Root>

Accessibility:

Related Components:


C-45: TextField.Input

Category: Form

Library: Custom

Platform: iOS + Android

Summary: The actual text input element within a TextField.Root, with support for all React Native TextInput props plus an accessible label.

When to Use:

Props:

Prop Type Required Default Description
label string Yes Accessible label for the input (used as accessibilityLabel)
value string No Controlled input value
onChangeText (text: string) => void No Callback fired on text change
keyboardType 'default' | 'email-address' | 'phone-pad' | 'numeric' | 'decimal-pad' No 'default' Keyboard type
returnKeyType 'next' | 'done' | 'go' | 'search' No Return key label
onSubmitEditing () => void No Callback when the return key is pressed
secureTextEntry boolean No false Hides input for passwords
autoCapitalize 'none' | 'sentences' | 'words' | 'characters' No 'sentences' Auto-capitalization behavior
autoCorrect boolean No true Whether autocorrect is enabled
autoComplete string No Autofill hint
autoFocus boolean No false Whether to focus the input on mount
multiline boolean No false Whether the input supports multiple lines
maxLength number No Maximum character length
editable boolean No true Whether the input is editable
accessibilityHint string No Additional context for screen readers
blurOnSubmit boolean No true Whether to blur on submit

Platform-Specific Behavior:

Behavior iOS Android Notes
returnKeyType Supported Supported Controls the keyboard's return key label
enablesReturnKeyAutomatically Supported Not supported iOS-only prop to disable return key when empty

Composition:

<TextField.Root>
  <TextField.Input
    label="Password"
    secureTextEntry
    returnKeyType="done"
    onSubmitEditing={onPressNext}
    accessibilityHint="Enter your account password"
  />
</TextField.Root>

Accessibility:

Related Components:


C-46: TextField.LabelText

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Visible label rendered above a text field, providing both visual and programmatic association between the label and the input.

Props:

Prop Type Required Default Description
children React.ReactNode Yes Label text content

Composition:

<TextField.LabelText>
  <Trans>Email address</Trans>
</TextField.LabelText>
<TextField.Root>
  <TextField.Input label="Email address" keyboardType="email-address" />
</TextField.Root>

Related Components:


C-47: TextField.Icon

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Leading icon rendered inside a TextField.Root, providing a visual category indicator for the input.

Props:

Prop Type Required Default Description
icon React.ComponentType Yes Icon component to render

Composition:

<TextField.Root>
  <TextField.Icon icon={Lock} />
  <TextField.Input label="Password" secureTextEntry />
</TextField.Root>

Accessibility:

Related Components:


C-48: TextField.SuffixText

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Right-aligned suffix text rendered inside a TextField.Root, used for character counters or unit labels.

Props:

Prop Type Required Default Description
label string No Accessible label for the suffix
children React.ReactNode Yes Suffix content

Composition:

<TextField.Root>
  <TextField.Input label="Pack name" value={name} onChangeText={setName} />
  <TextField.SuffixText label={`${name.length} out of 50`}>
    {name.length}/50
  </TextField.SuffixText>
</TextField.Root>

Related Components:


C-49: Toggle.Group

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Container managing a group of checkbox or radio toggle items, enforcing single-selection (radio) or multi-selection (checkbox) behavior.

When to Use:

Props:

Prop Type Required Default Description
type 'checkbox' | 'radio' Yes Selection mode
values string[] Yes Currently selected values
onChange (values: string[]) => void Yes Callback when selection changes
label string Yes Accessible label for the group
disabled boolean No false Whether the group is non-interactive
children React.ReactNode Yes Toggle.Item children

Variants / Visual States:

State/Variant Trigger Visual Behavior
Checkbox type="checkbox" Multiple items can be selected
Radio type="radio" Only one item can be selected
Disabled disabled={true} All items are non-interactive

Composition:

<Toggle.Group
  type="checkbox"
  values={channels}
  onChange={onChangeChannels}
  label={_(msg`Select your preferred notification channels`)}
>
  <Toggle.Item name="push" label={_(msg`Receive push notifications`)}>
    <Toggle.LabelText>Push notifications</Toggle.LabelText>
    <Toggle.Platform />
  </Toggle.Item>
  <Toggle.Item name="list" label={_(msg`Receive in-app notifications`)}>
    <Toggle.LabelText>In-app notifications</Toggle.LabelText>
    <Toggle.Platform />
  </Toggle.Item>
</Toggle.Group>

Accessibility:

Related Components:


C-50: Toggle.Item

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Individual selectable item within a Toggle.Group, providing interaction state context to its children.

Props:

Prop Type Required Default Description
name string Yes The value this item represents in the group
label string Yes Accessible label for this item
type 'checkbox' | 'radio' No Overrides the group type for this item
disabled boolean No false Whether this item is non-interactive
children React.ReactNode Yes Toggle.LabelText, Toggle.Platform, or Toggle.Radio

Composition:

<Toggle.Item name="push" label={_(msg`Receive push notifications`)}>
  <Toggle.LabelText>Push notifications</Toggle.LabelText>
  <Toggle.Platform />
</Toggle.Item>

Related Components:


C-51: Toggle.Platform

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Platform-appropriate toggle control that renders as a native Switch on iOS/Android and a checkbox or toggle on web.

When to Use:

Composition:

<Toggle.Item name="haptics" label={_(msg`Disable haptic feedback`)}>
  <Toggle.LabelText>Disable haptic feedback</Toggle.LabelText>
  <Toggle.Platform />
</Toggle.Item>

Platform-Specific Behavior:

Behavior iOS Android Notes
Appearance Native Switch Native Switch Web renders a checkbox or custom toggle

Related Components:


C-52: Toggle.Radio

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Radio button indicator rendered within a Toggle.Item in a radio-type Toggle.Group.

Composition:

<Toggle.Group type="radio" values={[sort]} onChange={setSort} label="Sort replies by">
  <Toggle.Item name="top" label={_(msg`Top replies first`)}>
    <Toggle.Radio />
    <Toggle.LabelText>Top replies first</Toggle.LabelText>
  </Toggle.Item>
</Toggle.Group>

Related Components:


C-53: Toggle.LabelText

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Styled label text rendered within a Toggle.Item, associated with the toggle control.

Props:

Prop Type Required Default Description
children React.ReactNode Yes Label text content

Related Components:


C-54: Select.Root

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Dropdown/select control for single-value selection from a list of options.

When to Use:

Props:

Prop Type Required Default Description
value string Yes Currently selected value
onChange (value: string) => void Yes Callback when selection changes
label string Yes Accessible label for the select
children React.ReactNode Yes Select.Trigger and Select.Content

Composition:

<Select.Root
  value={langPrefs.appLanguage}
  onChange={onChangeAppLanguage}
  label={_(msg`Select app language`)}
>
  <Select.Trigger>
    <Select.ValueText />
  </Select.Trigger>
  <Select.Content>
    {APP_LANGUAGES.map(lang => (
      <Select.Item key={lang.code2} value={lang.code2} label={lang.name} />
    ))}
  </Select.Content>
</Select.Root>

Accessibility:

Related Components:


C-55: SearchInput

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Search text input with a clear button, optional hotkey support, and focus management for the search shell.

When to Use:

Props:

Prop Type Required Default Description
value string Yes Controlled input value
onChangeText (text: string) => void Yes Callback on text change
onClearText () => void No Callback when the clear button is pressed
onFocus () => void No Callback when the input gains focus
onSubmitEditing () => void No Callback when the return key is pressed
placeholder string No Placeholder text
hotkey boolean No false Whether a keyboard shortcut focuses this input
ref React.RefObject<TextInput> No Ref for programmatic focus control

Composition:

<SearchInput
  ref={textInput}
  value={searchText}
  onChangeText={onChangeText}
  onClearText={onPressClearQuery}
  onFocus={onSearchInputFocus}
  onSubmitEditing={onSubmit}
  placeholder="Search..."
  hotkey
/>

Accessibility:

Related Components:


C-56: SegmentedControl.Root

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Radio-style segmented button group where the user selects one option from a horizontally arranged set.

When to Use:

Props:

Prop Type Required Default Description
values string[] Yes Currently selected values (typically one item for radio behavior)
onChange (values: string[]) => void Yes Callback when selection changes
type 'radio' | 'checkbox' No 'radio' Selection mode
label string Yes Accessible label for the group
children React.ReactNode Yes SegmentedControl.Item children

Composition:

<SegmentedControl.Root
  values={[colorMode]}
  onChange={values => setColorMode(values[0])}
  type="radio"
  label={_(msg`Color mode`)}
>
  <SegmentedControl.Item value="system" label={_(msg`System`)}>
    <SegmentedControl.ItemText>System</SegmentedControl.ItemText>
  </SegmentedControl.Item>
  <SegmentedControl.Item value="light" label={_(msg`Light`)}>
    <SegmentedControl.ItemText>Light</SegmentedControl.ItemText>
  </SegmentedControl.Item>
  <SegmentedControl.Item value="dark" label={_(msg`Dark`)}>
    <SegmentedControl.ItemText>Dark</SegmentedControl.ItemText>
  </SegmentedControl.Item>
</SegmentedControl.Root>

Related Components:


C-57: SegmentedControl.Item

Category: Form

Library: Custom

Platform: iOS + Android

Summary: Individual segment within a SegmentedControl.Root.

Props:

Prop Type Required Default Description
value string Yes The value this segment represents
label string Yes Accessible label for this segment
children React.ReactNode Yes Segment content (typically SegmentedControl.ItemText)

Related Components:


6. Feedback Components

C-58: Toast

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Transient notification message displayed at the bottom of the screen to confirm actions or report errors, invoked imperatively via Toast.show().

When to Use:

When NOT to Use:

Props (via Toast.show()):

Prop Type Required Default Description
message string Yes The message to display
type 'default' | 'error' No 'default' Visual style of the toast

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Toast.show(message) Neutral toast at bottom of screen
Error Toast.show(message, { type: 'error' }) Red-tinted error toast

Composition:

// Imperative usage — not rendered in JSX
Toast.show('Profile updated')
Toast.show('Failed to save changes', { type: 'error' })

Accessibility:

Known Issues:

Related Components:


C-59: Prompt.Basic

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Simple confirmation dialog with a title, description, and confirm/cancel buttons, used for destructive action confirmations.

When to Use:

Props:

Prop Type Required Default Description
control PromptControlProps Yes Control object from Prompt.usePromptControl()
title string Yes Dialog title
description string Yes Dialog description
onConfirm () => void Yes Callback when the confirm button is pressed
confirmButtonCta string No "Confirm" Label for the confirm button
isDestructive boolean No false Whether the confirm button uses destructive styling
cancelButtonCta string No "Cancel" Label for the cancel button

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default control.open() Modal dialog with title, description, buttons
Destructive isDestructive={true} Confirm button in red/negative color

Composition:

const deleteControl = Prompt.usePromptControl()

<Prompt.Basic
  control={deleteControl}
  title="Delete account"
  description="This action cannot be undone."
  onConfirm={onDeleteAccount}
  confirmButtonCta="Delete"
  isDestructive
/>

// Trigger:
<Button onPress={deleteControl.open}>
  <ButtonText>Delete account</ButtonText>
</Button>

Accessibility:

Related Components:


C-60: Prompt.Outer

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Outer wrapper for a prompt dialog, used with Prompt.Basic when the prompt needs to be rendered outside the normal component tree (e.g., at the screen root).

Props:

Prop Type Required Default Description
control PromptControlProps Yes Control object from Prompt.usePromptControl()
children React.ReactNode Yes Prompt.Basic content

Related Components:


C-61: ErrorScreen

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Full-page error display with an exclamation icon, title, message, optional technical details, and an optional retry button.

When to Use:

Props:

Prop Type Required Default Description
title string Yes Bold error heading
message string Yes Descriptive error message
details string No Technical error details shown in a bordered box
onPressTryAgain () => void No Retry callback; renders a retry button when provided
showHeader boolean No false Whether to render a Layout.Header.Outer with a back button
testID string No Test identifier

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Rendered Icon, title, message
With details details provided Technical details box below message
With retry onPressTryAgain provided "Try again" button below details
With header showHeader={true} Navigation header with back button

Composition:

{isError && (
  <ErrorScreen
    title="Oops!"
    message="We couldn't load that page."
    details={cleanError(error)}
    onPressTryAgain={refetch}
  />
)}

Accessibility:

Related Components:


C-62: FormError

Category: Feedback

Library: Custom

Platform: iOS + Android

Summary: Inline error message displayed within a form, typically below the form fields and above the submit button.

When to Use:

Props:

Prop Type Required Default Description
message string Yes The error message to display

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default message is non-empty Red error text
Hidden message is empty Not rendered

Composition:

<FormError message={error} />

Accessibility:

Related Components:


7. Navigation Components

C-63: TabBar

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Horizontal tab label row rendered above a Pager, allowing users to tap to switch between tabs.

When to Use:

Props:

Prop Type Required Default Description
items string[] Yes Array of tab label strings
selectedPage number Yes Index of the currently selected tab
onSelect (index: number) => void Yes Callback when a tab is tapped
testID string No Test identifier

Composition:

<Pager
  renderTabBar={props => (
    <Layout.Center>
      <TabBar
        items={['Top', 'Latest', 'People', 'Feeds']}
        selectedPage={activeTab}
        onSelect={onPageSelected}
        {...props}
      />
    </Layout.Center>
  )}
>
  {/* tab content */}
</Pager>

Accessibility:

Related Components:


C-64: Pager

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Swipeable horizontal tab container that manages multiple tab views and exposes onPageSelected for tab change events.

When to Use:

Props:

Prop Type Required Default Description
renderTabBar (props: PagerTabBarProps) => JSX.Element Yes Render prop for the tab bar
onPageSelected (index: number) => void No Callback when the active page changes
initialPage number No 0 Initial active page index
children React.ReactNode Yes Tab content panels

Composition:

<Pager
  renderTabBar={props => <TabBar items={['Top', 'Latest']} {...props} />}
  onPageSelected={setActiveTab}
  initialPage={0}
>
  <View>{/* Top tab content */}</View>
  <View>{/* Latest tab content */}</View>
</Pager>

Accessibility:

Related Components:


C-65: PagerWithHeader

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Tabbed pager with a sticky header and synchronized scroll behavior, used for profile screens and the Starter Pack detail screen.

When to Use:

Props:

Prop Type Required Default Description
items string[] Yes Tab label strings
renderHeader () => JSX.Element Yes Render prop for the sticky header content
onPageSelected (index: number) => void No Callback when the active page changes
initialPage number No 0 Initial active page index
children React.ReactNode Yes Tab content panels

Related Components:


C-66: ProfileMenu

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Three-dot overflow menu for profile actions including report, mute, block, share, and list management.

When to Use:

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewDetailed Yes Profile data
moderation ModerationDecision Yes Moderation decision
blockInfo BlockInfo No Block relationship information

Composition:

<ProfileMenu
  profile={profile}
  moderation={moderation}
  blockInfo={blockInfo}
/>

Accessibility:

Related Components:


C-67: OverflowMenu (ConvoMenu)

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Context menu for conversation actions including mute, block, report, leave, and mark as read.

When to Use:

Props:

Prop Type Required Default Description
convo ChatBskyConvoDefs.ConvoView Yes Conversation data
profile AppBskyActorDefs.ProfileViewBasic Yes The other user's profile
blockInfo BlockInfo No Block relationship information
latestReportableMessage MessageView No Most recent reportable message

Composition:

<ConvoMenu
  convo={convo}
  profile={otherUser}
  blockInfo={blockInfo}
  latestReportableMessage={latestReportableMessage}
/>

Related Components:


C-68: SettingsList.LinkItem

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Tappable settings row that navigates to another screen when pressed, with an optional badge and a trailing chevron icon.

When to Use:

Props:

Prop Type Required Default Description
to string | object Yes Navigation destination (route name or path)
label string Yes Accessible label for the item
accessibilityHint string No Additional accessibility context
children React.ReactNode Yes Row content (icon, text, badge)

Composition:

<SettingsList.LinkItem
  to="/settings/app-passwords"
  label={_(msg`App passwords`)}
>
  <SettingsList.ItemIcon icon={KeyIcon} />
  <SettingsList.ItemText>App passwords</SettingsList.ItemText>
  {appPasswords?.length > 0 && (
    <SettingsList.BadgeText>{appPasswords.length}</SettingsList.BadgeText>
  )}
</SettingsList.LinkItem>

Accessibility:

Related Components:


C-69: SettingsList.PressableItem

Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Tappable settings row that triggers an action when pressed (not navigation), with hover/press visual feedback.

When to Use:

Props:

Prop Type Required Default Description
onPress () => void Yes Action callback
label string Yes Accessible label for the item
destructive boolean No false Whether the item uses destructive styling
disabled boolean No false Whether the item is non-interactive
children React.ReactNode Yes Row content

Composition:

<SettingsList.PressableItem
  onPress={signOutControl.open}
  label={_(msg`Sign out`)}
  destructive
>
  <SettingsList.ItemIcon icon={SignOutIcon} />
  <SettingsList.ItemText>Sign out</SettingsList.ItemText>
</SettingsList.PressableItem>

Related Components:


8. Gesture & Touch Components

C-70: GestureActionView

Category: Gesture & Touch

Library: Custom (react-native-gesture-handler, react-native-reanimated)

Platform: iOS + Android

Summary: Horizontal swipe gesture wrapper that reveals action buttons (mark as read, delete) at configurable pixel thresholds, with haptic feedback and animated background color transitions.

When to Use:

Props:

Prop Type Required Default Description
actions GestureActions Yes Object defining leftFirst, leftSecond, rightFirst, rightSecond action configurations
children React.ReactNode Yes The list item content that slides

GestureAction shape:

Field Type Description
threshold number Pixel distance to trigger this action
color string Background color shown when this action is active
icon React.ComponentType Icon rendered in the background
action () => void Callback fired when the gesture ends at this threshold

Platform-Specific Behavior:

Behavior iOS Android Notes
Haptic feedback Fired on threshold crossing Fired on threshold crossing Via useHaptics()
Reduced motion Icon scale animation suppressed Icon scale animation suppressed Respects useReducedMotion()

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default No swipe Content at rest position
First threshold Swipe past leftFirst.threshold Background color + icon appear
Second threshold Swipe past leftSecond.threshold Background color changes to second action color

Composition:

<GestureActionView
  actions={{
    rightFirst: {
      threshold: 120,
      color: t.palette.primary_500,
      icon: CheckIcon,
      action: () => markAsRead({ convoId: convo.id }),
    },
    rightSecond: {
      threshold: 225,
      color: t.palette.negative_500,
      icon: TrashIcon,
      action: () => leaveConvoPrompt.open(),
    },
  }}
>
  <ChatListItem convo={convo} />
</GestureActionView>

Accessibility:

Known Issues:

Related Components:


C-71: DraggableScrollView

Category: Gesture & Touch

Library: Custom (useDraggableScroll hook)

Platform: Web

Summary: Horizontal ScrollView with click-and-drag scrolling for desktop/web users, with user-select: none applied to prevent text selection during drag.

When to Use:

Props:

Prop Type Required Default Description
ref React.RefObject<ScrollView> No Forwarded ref for programmatic scroll control
style StyleProp<ViewStyle> No Additional styles
children React.ReactNode Yes Horizontally scrollable content

Platform-Specific Behavior:

Behavior iOS Android Notes
Drag scroll Not applicable Not applicable Web-only feature
user-select: none Not applied Not applied Applied only on web via web(a.user_select_none)

Composition:

<DraggableScrollView horizontal showsHorizontalScrollIndicator={false}>
  {videoCards.map(card => <CompactVideoPostCard key={card.uri} post={card} />)}
</DraggableScrollView>

Known Issues:

Related Components:


C-72: AnimatedPressable

Category: Gesture & Touch

Library: Custom (react-native-reanimated)

Platform: iOS + Android

Summary: Pressable with Reanimated-driven opacity or scale animation on press, used for interactive overlay elements.

When to Use:

Props:

Prop Type Required Default Description
onPress () => void No Press callback
style StyleProp<ViewStyle> No Additional styles
children React.ReactNode Yes Content

Related Components:


C-73: SubtleHover

Category: Gesture & Touch

Library: Custom

Platform: iOS + Android

Summary: Low-opacity hover/press highlight overlay that provides visual feedback without a heavy highlight effect.

When to Use:

Props:

Prop Type Required Default Description
hover boolean Yes Whether the hover/press state is active
children React.ReactNode Yes Content

Composition:

<Link to={trend.link}>
  {({ hovered, pressed }) => (
    <>
      <SubtleHover hover={hovered || pressed} />
      <TrendingTopic trend={trend} />
    </>
  )}
</Link>

Related Components:


C-74: BlockDrawerGesture

Category: Gesture & Touch

Library: Custom (react-native-gesture-handler)

Platform: iOS + Android

Summary: Wrapper component that prevents the navigation drawer from intercepting horizontal swipe gestures within its children, enabling horizontal scroll within a drawer-enabled layout.

When to Use:

Props:

Prop Type Required Default Description
children React.ReactNode Yes Content that should receive horizontal swipe gestures

Composition:

<BlockDrawerGesture>
  <ScrollView horizontal showsHorizontalScrollIndicator={false}>
    {/* horizontal content */}
  </ScrollView>
</BlockDrawerGesture>

Related Components:


9. Utility Components

C-75: Dialog.Outer

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Root dialog container that presents a modal overlay with native or web presentation options.

When to Use:

Props:

Prop Type Required Default Description
control DialogControlProps Yes Control object from useDialogControl()
nativeOptions object No Native-specific presentation options (e.g., { sheet: { snapPoints: [...] } })
webOptions object No Web-specific options (e.g., { onBackgroundPress })
children React.ReactNode Yes Dialog content

Composition:

const control = useDialogControl()

<Dialog.Outer control={control}>
  <Dialog.ScrollableInner label={_(msg`Edit profile`)}>
    {/* dialog content */}
  </Dialog.ScrollableInner>
</Dialog.Outer>

// Trigger:
<Button onPress={control.open}>
  <ButtonText>Edit Profile</ButtonText>
</Button>

Accessibility:

Related Components:


C-76: Dialog.ScrollableInner

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Scrollable content area within a dialog, with a header slot for title and action buttons.

Props:

Prop Type Required Default Description
label string Yes Accessible label for the dialog
children React.ReactNode Yes Dialog body content
style StyleProp<ViewStyle> No Additional styles

Composition:

<Dialog.Outer control={control}>
  <Dialog.ScrollableInner label={_(msg`Change Handle`)}>
    <Dialog.Header>
      <Dialog.HeaderText>Change Handle</Dialog.HeaderText>
    </Dialog.Header>
    {/* form content */}
  </Dialog.ScrollableInner>
</Dialog.Outer>

Related Components:


C-77: Dialog.Input

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Styled text input for use inside a dialog, with the same props as TextField.Input but styled for the dialog context.

Props:

Prop Type Required Default Description
label string Yes Accessible label
value string No Controlled value
onChangeText (text: string) => void No Change callback
testID string No Test identifier
autoFocus boolean No false Whether to focus on mount

Composition:

<Dialog.Input
  label={_(msg`Confirmation code`)}
  value={confirmCode}
  onChangeText={setConfirmCode}
  testID="confirmationCode"
  autoFocus
/>

Related Components:


C-78: Portal / createPortalGroup

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: React context-based teleportation mechanism that renders child content in a different position in the component tree, used in SettingsList.Group and the Onboarding Layout.

When to Use:

Props (via createPortalGroup()):

Returns { Provider, Portal, Outlet }:

Composition:

// In SettingsList.Group:
const { Provider, Portal, Outlet } = createPortalGroup()

<Provider>
  <View style={styles.headerRow}>
    <Outlet /> {/* ItemIcon and ItemText render here */}
  </View>
  <View style={styles.contentRow}>
    {children} {/* Toggle items render here */}
  </View>
</Provider>

// Usage:
<SettingsList.Group>
  <SettingsList.ItemIcon icon={BellIcon} /> {/* Teleported to header row */}
  <SettingsList.ItemText>Notifications</SettingsList.ItemText> {/* Teleported */}
  <Toggle.Item name="push">...</Toggle.Item> {/* Stays in content row */}
</SettingsList.Group>

Related Components:


C-79: ScreenTransition

Category: Utility

Library: Custom (react-native-reanimated)

Platform: iOS + Android

Summary: Directional slide/fade animation wrapper for wizard step transitions, driven by a direction prop.

When to Use:

Props:

Prop Type Required Default Description
direction 'Forward' | 'Backward' Yes Animation direction
enabledWeb boolean No true Whether to apply the animation on web
children React.ReactNode Yes Step content

Composition:

<ScreenTransition direction={state.transitionDirection}>
  <StepDetails state={state} dispatch={dispatch} />
</ScreenTransition>

Related Components:


C-80: GifView

Category: Utility

Library: expo-bluesky-gif-view (Custom Expo Module)

Platform: iOS + Android

Summary: Native GIF playback component with imperative playAsync(), pauseAsync(), and toggleAsync() methods, and a prefetchAsync() static method for pre-loading.

When to Use:

Props:

Prop Type Required Default Description
source string No URL of the GIF to play
autoplay boolean No false Whether to begin playback immediately on load
placeholderSource string No URL of a placeholder image shown while loading
onPlayerStateChange (event: GifViewStateChangeEvent) => void No Callback fired when isPlaying or isLoaded changes
style StyleProp<ViewStyle> No Additional styles

Imperative Methods (via ref):

Method Returns Description
playAsync() Promise<void> Starts or resumes GIF playback
pauseAsync() Promise<void> Pauses GIF playback
toggleAsync() Promise<void> Toggles between play and pause

Static Methods:

Method Returns Description
GifView.prefetchAsync(sources: string[]) Promise<void> Pre-loads GIF data into the native cache

Known Issues:


C-81: EmojiPicker

Category: Utility

Library: expo-emoji-picker (Custom Expo Module)

Platform: iOS + Android

Summary: Native emoji picker view that fires onEmojiSelected with the selected emoji string when the user taps an emoji.

When to Use:

Props:

Prop Type Required Default Description
onEmojiSelected (emoji: string) => void Yes Callback fired with the selected emoji character
style StyleProp<ViewStyle> No Additional styles

Composition:

<EmojiPicker
  onEmojiSelected={emoji => insertEmoji(emoji)}
  style={{ flex: 1 }}
/>

Accessibility:


C-82: ExpoScrollForwarderView

Category: Utility

Library: expo-scroll-forwarder (Custom Expo Module)

Platform: iOS + Android (native passthrough) / Web (null render)

Summary: Forwards scroll/gesture events from a non-scrollable area to a designated target ScrollView, enabling collapsible headers and custom scroll-linked animations.

When to Use:

Props:

Prop Type Required Default Description
scrollViewTag number No Native node handle of the target ScrollView
children React.ReactNode Yes Content that should forward its scroll events

Platform-Specific Behavior:

Behavior iOS Android Notes
Scroll forwarding Native implementation Native implementation Web variant renders null

C-83: AgeRestrictedScreen

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Wrapper component that gates content behind age assurance, rendering an age-restriction notice if the user has not completed age verification.

When to Use:

Props:

Prop Type Required Default Description
children React.ReactNode Yes The gated screen content

Variants / Visual States:

State/Variant Trigger Visual Behavior
Allowed Age assurance passed Renders children
Restricted Age assurance not passed Renders age-restriction notice with aaCopy.chatsInfoText

Related Components:


C-84: SettingsList.Container

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Full-width vertical list wrapper with vertical padding, used as the outermost container for all settings screen content.

Props:

Prop Type Required Default Description
children React.ReactNode Yes Settings items and groups
style StyleProp<ViewStyle> No Additional styles

Composition:

<Layout.Content>
  <SettingsList.Container>
    <SettingsList.Item>...</SettingsList.Item>
    <SettingsList.Divider />
    <SettingsList.Group>...</SettingsList.Group>
  </SettingsList.Container>
</Layout.Content>

Related Components:


C-85: SettingsList.Group

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Grouped settings section that uses a Portal mechanism to render SettingsList.ItemIcon and SettingsList.ItemText in a header row, with remaining children in a content row below.

Props:

Prop Type Required Default Description
iconInset boolean No true Whether to apply icon-width indentation to the content row
destructive boolean No false Whether the group uses destructive styling
children React.ReactNode Yes SettingsList.ItemIcon, SettingsList.ItemText, and content items

Composition:

<SettingsList.Group iconInset={false}>
  <SettingsList.ItemText>Sort replies</SettingsList.ItemText>
  <Text>Sort replies to the same post by:</Text>
  <Toggle.Group type="radio" values={[sort]} onChange={setSort} label="Sort">
    <Toggle.Item name="top" label="Top replies first">...</Toggle.Item>
  </Toggle.Group>
</SettingsList.Group>

Related Components:


C-86: SettingsList.Item

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Horizontal row layout for a single settings entry, providing consistent padding, minimum height, and optional icon inset.

Props:

Prop Type Required Default Description
destructive boolean No false Whether the item uses destructive styling
children React.ReactNode Yes Row content
style StyleProp<ViewStyle> No Additional styles

Related Components:


C-87: SettingsList.Divider

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Horizontal rule separator between settings groups, with a low-contrast border and vertical margin.

Props:

Prop Type Required Default Description
style StyleProp<ViewStyle> No Additional styles

Composition:

<SettingsList.Container>
  <SettingsList.Item>...</SettingsList.Item>
  <SettingsList.Divider />
  <SettingsList.Item>...</SettingsList.Item>
</SettingsList.Container>

Related Components:


C-88: PreferenceControls

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Reusable toggle controls for notification channel (push/in-app) and filter (everyone/follows) preferences, used across all notification settings screens.

When to Use:

Props:

Prop Type Required Default Description
name string Yes The preference key (e.g., "reply", "like", "follow")
preference AppBskyNotificationDefs.Preference | FilterablePreference | undefined No The current preference object; undefined shows a loading spinner
syncOthers string[] No [] Additional preference keys to update simultaneously
allowDisableInApp boolean No true Whether to show the in-app notification toggle

Variants / Visual States:

State/Variant Trigger Visual Behavior
Loading preference is undefined Centered Loader spinner
Channels only preference is Preference Push + in-app toggles
With filter preference is FilterablePreference Push + in-app toggles + "From" radio group

Composition:

<PreferenceControls
  name="reply"
  preference={preferences?.reply}
  allowDisableInApp
/>

Accessibility:

Known Issues:

Related Components:


C-89: ItemTextWithSubtitle

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Title and subtitle text block used within a settings item, with optional bold/large title styling.

Props:

Prop Type Required Default Description
bold boolean No false Whether to render the title at text_lg with font_semi_bold
children React.ReactNode Yes Title and subtitle content

Composition:

<SettingsList.Item>
  <SettingsList.ItemIcon icon={BellRingingIcon} />
  <ItemTextWithSubtitle bold>
    <Text>Activity from others</Text>
    <Text>Get notified about posts and replies from accounts you choose.</Text>
  </ItemTextWithSubtitle>
</SettingsList.Item>

Related Components:


C-90: InlineLinkText

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Tappable hyperlink rendered inline within a block of text, supporting both internal navigation and external URLs.

When to Use:

Props:

Prop Type Required Default Description
to string | object Yes Navigation destination or URL
label string Yes Accessible label for the link
style StyleProp<TextStyle> No Additional text styles
onPress (e: GestureResponderEvent) => boolean | void No Custom press handler
children React.ReactNode Yes Link text content

Composition:

<Text>
  By continuing, you agree to our{' '}
  <InlineLinkText to="https://bsky.social/about/support/tos" label="Terms of Service">
    Terms of Service
  </InlineLinkText>
  {' '}and{' '}
  <InlineLinkText to="https://bsky.social/about/support/privacy-policy" label="Privacy Policy">
    Privacy Policy
  </InlineLinkText>.
</Text>

Accessibility:

Related Components:


Category: Navigation

Library: Custom

Platform: iOS + Android

Summary: Navigation link component that renders as an anchor element on web and uses React Navigation on native, supporting both internal routes and external URLs.

When to Use:

Props:

Prop Type Required Default Description
to string | object Yes Navigation destination (route path or object)
label string No Accessible label
accessibilityHint string No Additional accessibility context
action 'push' | 'replace' | 'navigate' No 'push' Navigation action type
children React.ReactNode | ((state: { hovered: boolean; pressed: boolean }) => React.ReactNode) Yes Link content or render prop
style StyleProp<ViewStyle> No Additional styles

Composition:

<Link to={makeProfileLink(profile)} label={profile.handle}>
  <UserAvatar profile={profile} size={42} />
</Link>

Known Issues:

Related Components:


C-92: Button

Category: Gesture & Touch

Library: Custom

Platform: iOS + Android

Summary: Pressable button with configurable variant, color, size, shape, and accessible label props, used throughout the application for all interactive button elements.

When to Use:

Props:

Prop Type Required Default Description
label string Yes Accessible label (used as accessibilityLabel)
onPress () => void No Press callback
variant 'solid' | 'outline' | 'ghost' No 'solid' Visual style variant
color 'primary' | 'secondary' | 'secondary_inverted' | 'negative' | 'primary_subtle' No 'primary' Color scheme
size 'tiny' | 'small' | 'medium' | 'large' No 'medium' Button size
shape 'default' | 'round' | 'square' No 'default' Border radius shape
disabled boolean No false Whether the button is non-interactive
accessibilityHint string No Additional accessibility context
testID string No Test identifier
style StyleProp<ViewStyle> No Additional styles
children React.ReactNode | ((state: { hovered: boolean; pressed: boolean }) => React.ReactNode) Yes Button content or render prop

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Initial render Solid background, themed color
Hovered Pointer hover (web) Slightly darker background
Pressed Touch/click Darker background
Disabled disabled={true} Reduced opacity, non-interactive

Composition:

<Button
  label={_(msg`Save changes`)}
  onPress={onSaveChanges}
  variant="solid"
  color="primary"
  size="large"
  disabled={!hasUnsavedChanges || isPending}
>
  {isPending && <ButtonIcon icon={Loader} />}
  <ButtonText>Save changes</ButtonText>
</Button>

Accessibility:

Known Issues:

Related Components:


C-93: ButtonIcon

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Icon slot within a Button component, rendering an icon inline with button text.

Props:

Prop Type Required Default Description
icon React.ComponentType Yes Icon component to render
position 'left' | 'right' No 'left' Position relative to button text

Composition:

<Button label="Add" onPress={onAdd}>
  <ButtonIcon icon={PlusIcon} />
  <ButtonText>Add</ButtonText>
</Button>

Related Components:


C-94: ButtonText

Category: Utility

Library: Custom

Platform: iOS + Android

Summary: Text slot within a Button component, styled to match the button's color and size.

Props:

Prop Type Required Default Description
children React.ReactNode Yes Button label text
style StyleProp<TextStyle> No Additional text styles

Composition:

<Button label="Sign in" onPress={onSignIn}>
  <ButtonText>Sign in</ButtonText>
</Button>

Related Components:


Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Bluesky icon mark SVG component, rendered at a configurable width with theme-aware fill color.

Props:

Prop Type Required Default Description
width number No 32 Width of the logo in logical pixels
fill string No Fill color override
style StyleProp<ViewStyle> No Additional styles

Composition:

<Logo width={76} fill="white" />

Related Components:


C-96: Logotype

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Bluesky wordmark SVG component, rendered at a configurable width with theme-aware fill color.

Props:

Prop Type Required Default Description
width number No 91 Width of the wordmark in logical pixels
fill string No Fill color override

Composition:

<Logotype width={91} fill="white" />

Related Components:


C-97: UserAvatar

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Profile picture component with moderation support, live status indicator ring, and type variants (circular for users, rectangular for labelers).

When to Use:

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewBasic | ProfileViewDetailed Yes Profile data
size number No 42 Avatar size in logical pixels
moderation ModerationDecision No Moderation decision for blur/hide
type 'user' | 'labeler' No 'user' Shape variant (circle vs. rectangle)
live boolean No false Whether to show a live status ring
style StyleProp<ViewStyle> No Additional styles

Variants / Visual States:

State/Variant Trigger Visual Behavior
Default Normal user Circular avatar image
Labeler type="labeler" Rectangular avatar with rounded corners
Live live={true} Red border ring around avatar
Moderated Moderation blur Avatar may be blurred or hidden
Missing No avatar Placeholder color/icon

Composition:

<UserAvatar
  profile={profile}
  size={88}
  moderation={moderation}
  type={profile.associated?.labeler ? 'labeler' : 'user'}
  live={live.isActive}
/>

Accessibility:

Related Components:


C-98: UserBanner

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Profile banner image component with moderation support and type variants (user vs. labeler).

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewDetailed Yes Profile data
moderation ModerationDecision No Moderation decision for blur/hide
type 'user' | 'labeler' No 'user' Banner type variant

Composition:

<UserBanner
  profile={profile}
  moderation={moderation}
  type={profile.associated?.labeler ? 'labeler' : 'user'}
/>

Related Components:


C-99: EditableUserAvatar

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Avatar component with an edit affordance (pencil icon overlay) for use in profile editing dialogs.

Props:

Prop Type Required Default Description
profile AppBskyActorDefs.ProfileViewDetailed Yes Profile data
size number No 80 Avatar size in logical pixels
onSelectNewAvatar (img: ImageMeta | null) => void Yes Callback when a new avatar is selected or removed

Composition:

<EditableUserAvatar
  profile={profile}
  size={80}
  onSelectNewAvatar={onSelectNewAvatar}
/>

Related Components:


C-100: LiveIndicator

Category: Data Display

Library: Custom

Platform: iOS + Android

Summary: Animated live status indicator overlaid on a user avatar, showing a pulsing "LIVE" badge when a user is currently streaming.

When to Use:

Props:

Prop Type Required Default Description
size 'small' | 'large' No 'small' Size of the indicator

Composition:

{live.isActive && (
  <LiveIndicator size="large" />
)}

Related Components:


10. Component Composition Patterns

CP-01: Screen Shell Pattern

Components involved: C-01: Layout.Screen, C-02: Layout.Header.Outer, C-03: Layout.Header.BackButton, C-04: Layout.Header.Content, C-05: Layout.Header.TitleText, C-07: Layout.Header.Slot, C-09: Layout.Content

When to use: For any screen in the application that requires a standard header and scrollable content area.

The pattern:

// Standard screen shell with back button, title, and scrollable content
<Layout.Screen testID="myScreen">
  <Layout.Header.Outer>
    <Layout.Header.BackButton />
    <Layout.Header.Content>
      <Layout.Header.TitleText>
        <Trans>Screen Title</Trans>
      </Layout.Header.TitleText>
    </Layout.Header.Content>
    <Layout.Header.Slot />
  </Layout.Header.Outer>
  <Layout.Content>
    {/* Screen body content */}
  </Layout.Content>
</Layout.Screen>

Key rules:


CP-02: Settings List Pattern

Components involved: C-84: SettingsList.Container, C-85: SettingsList.Group, C-86: SettingsList.Item, C-87: SettingsList.Divider, C-68: SettingsList.LinkItem, C-69: SettingsList.PressableItem

When to use: For any settings screen that presents a list of configurable options.

The pattern:

<Layout.Content>
  <SettingsList.Container>
    {/* Navigation link to a sub-screen */}
    <SettingsList.LinkItem
      to="/settings/account"
      label={_(msg`Account`)}
    >
      <SettingsList.ItemIcon icon={PersonIcon} />
      <SettingsList.ItemText>Account</SettingsList.ItemText>
    </SettingsList.LinkItem>

    <SettingsList.Divider />

    {/* Action item that opens a dialog */}
    <SettingsList.PressableItem
      onPress={signOutControl.open}
      label={_(msg`Sign out`)}
      destructive
    >
      <SettingsList.ItemIcon icon={SignOutIcon} />
      <SettingsList.ItemText>Sign out</SettingsList.ItemText>
    </SettingsList.PressableItem>

    {/* Grouped items with a shared icon/label header */}
    <SettingsList.Group>
      <SettingsList.ItemIcon icon={BellIcon} />
      <SettingsList.ItemText>Notifications</SettingsList.ItemText>
      <Toggle.Item name="push" label={_(msg`Push notifications`)}>
        <Toggle.LabelText>Push notifications</Toggle.LabelText>
        <Toggle.Platform />
      </Toggle.Item>
    </SettingsList.Group>
  </SettingsList.Container>
</Layout.Content>

Key rules:


CP-03: Notification Preference Toggle Pattern

Components involved: C-88: PreferenceControls, C-49: Toggle.Group, C-50: Toggle.Item, C-51: Toggle.Platform, C-52: Toggle.Radio, C-36: Admonition, C-42: Loader

When to use: For any notification settings screen that allows users to configure push and in-app notification channels and optional sender filters.

The pattern:

// In a notification settings screen:
const { data: preferences, isError } = useNotificationSettingsQuery()

<Layout.Content>
  <SettingsList.Container>
    {/* Header item with icon and description */}
    <SettingsList.Item>
      <SettingsList.ItemIcon icon={BubbleIcon} />
      <ItemTextWithSubtitle bold>
        <Text>Replies</Text>
        <Text>Get notifications when people reply to your posts.</Text>
      </ItemTextWithSubtitle>
    </SettingsList.Item>

    {/* Error state */}
    {isError && (
      <Admonition type="error">
        <Trans>Failed to load notification settings.</Trans>
      </Admonition>
    )}

    {/* Preference controls (handles loading, channels, and filter) */}
    {!isError && (
      <PreferenceControls
        name="reply"
        preference={preferences?.reply}
      />
    )}
  </SettingsList.Container>
</Layout.Content>

Key rules:


CP-04: Infinite Scroll List Pattern

Components involved: C-17: List, C-18: ListFooter, C-42: Loader, C-35: EmptyState

When to use: For any screen that displays a paginated list of items fetched from the API.

The pattern:

const { data, isLoading, isError, error, isFetchingNextPage, hasNextPage, fetchNextPage, refetch } =
  useMyInfiniteQuery()

const items = useMemo(
  () => data?.pages.flatMap(page => page.items) ?? [],
  [data]
)

const onEndReached = useCallback(() => {
  if (!isFetchingNextPage && hasNextPage && !isError) {
    fetchNextPage()
  }
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])

<List
  data={items}
  renderItem={({ item }) => <MyItemCard item={item} />}
  keyExtractor={item => item.id}
  onRefresh={refetch}
  refreshing={isPTRing}
  onEndReached={onEndReached}
  onEndReachedThreshold={4}
  ListEmptyComponent={
    isLoading ? (
      <Loader size="xl" />
    ) : (
      <EmptyState message="No items found." />
    )
  }
  ListFooterComponent={
    <ListFooter
      isFetchingNextPage={isFetchingNextPage}
      error={cleanError(error)}
      onRetry={fetchNextPage}
    />
  }
  windowSize={11}
/>

Key rules:


CP-05: Wizard Step Pattern

Components involved: C-79: ScreenTransition, C-44: TextField.Root, C-45: TextField.Input, C-92: Button

When to use: For each step in a multi-step wizard (Starter Pack creation, Find Contacts flow, Onboarding).

The pattern:

// In a wizard step component:
const [state, dispatch] = useWizardState()

<ScreenTransition direction={state.transitionDirection}>
  <View style={[a.flex_1, a.px_xl]}>
    {/* Step content */}
    <TextField.LabelText>
      <Trans>Pack name</Trans>
    </TextField.LabelText>
    <TextField.Root>
      <TextField.Input
        label={_(msg`Pack name`)}
        value={state.name ?? ''}
        onChangeText={text => dispatch({ type: 'SetName', name: text })}
        placeholder="My starter pack"
      />
    </TextField.Root>
  </View>
</ScreenTransition>

Key rules:


CP-06: Dismissible Keyboard Pattern

Components involved: C-12: KeyboardAwareScrollView, C-44: TextField.Root, C-45: TextField.Input

When to use: For any form screen where the software keyboard may obscure input fields.

The pattern:

// Form screen with keyboard-aware scrolling
<KeyboardAwareScrollView
  keyboardShouldPersistTaps="handled"
  keyboardDismissMode="on-drag"
>
  <TextField.LabelText>Email</TextField.LabelText>
  <TextField.Root>
    <TextField.Input
      label="Email address"
      keyboardType="email-address"
      returnKeyType="next"
      onSubmitEditing={() => passwordRef.current?.focus()}
      autoCapitalize="none"
    />
  </TextField.Root>

  <TextField.LabelText>Password</TextField.LabelText>
  <TextField.Root>
    <TextField.Input
      ref={passwordRef}
      label="Password"
      secureTextEntry
      returnKeyType="done"
      onSubmitEditing={onSubmit}
    />
  </TextField.Root>
</KeyboardAwareScrollView>

Key rules:


CP-07: Profile Header Composition Pattern

Components involved: C-14: GrowableBanner, C-15: GrowableAvatar, C-13: StatusBarShadow, C-97: UserAvatar, C-98: UserBanner, C-100: LiveIndicator, C-33: RichText

When to use: For the profile header in the Profile screen, combining the banner, avatar, display name, handle, metrics, and bio.

The pattern:

// Profile header shell composition
<ProfileHeaderShell
  profile={profile}
  moderation={moderation}
  isPlaceholderProfile={isPlaceholderProfile}
  hideBackButton={hideBackButton}
>
  {/* Action buttons row */}
  <HeaderStandardButtons
    profile={profile}
    moderation={moderation}
    isMe={isMe}
    minimal={minimal}
    onFollow={onFollow}
    onUnfollow={onUnfollow}
  />

  {/* Display name */}
  <ProfileHeaderDisplayName profile={profile} moderation={moderation} />

  {/* Handle */}
  <ProfileHeaderHandle profile={profile} disableTaps={disableTaps} />

  {/* Metrics (self only) */}
  {isSelf && !isPlaceholderProfile && (
    <ProfileHeaderMetrics profile={profile} />
  )}

  {/* Bio */}
  {descriptionRT && !moderation.ui('profileView').blur && (
    <RichText value={descriptionRT} numberOfLines={15} enableTags />
  )}
</ProfileHeaderShell>

Key rules:


11. Glossary

React Native Core Primitives

View — React Native's primary layout container. Renders to a native UIView (iOS) or android.view.View (Android). Uses Flexbox for layout with flexDirection defaulting to 'column'. Not equivalent to HTML <div>.

Text — React Native's text rendering primitive. All text must be inside a Text component. Not equivalent to HTML <span> or <p>.

TextInput — React Native's text input primitive. Supports keyboardType, returnKeyType, secureTextEntry, autoCapitalize, autoCorrect, autoComplete, and onSubmitEditing.

ScrollView — React Native's scrollable container. All content is rendered at once (not virtualized). Use FlatList or the custom List component for large datasets.

FlatList — React Native's virtualized list component. Only renders visible items. The custom List component wraps Animated.FlatList from react-native-reanimated.

SectionList — React Native's virtualized list with section headers. Not directly used in the assessed screens; List is used instead.

Image — React Native's image component. The application uses expo-image's Image for better caching and performance.

Modal — React Native's full-screen modal overlay. Used in the Signup Queued screen.

ActivityIndicator — React Native's built-in loading spinner. Used in GrowableBanner for the pull-to-refresh spinner.

StyleSheet — React Native's style definition utility. Not equivalent to CSS classes; styles are JavaScript objects.

SafeAreaView — React Native's safe area container. The application uses useSafeAreaInsets() from react-native-safe-area-context instead.

KeyboardAvoidingView — React Native's keyboard avoidance container. The application uses KeyboardAwareScrollView from react-native-keyboard-controller instead.

StatusBar — React Native's status bar control. The application uses SystemBars from react-native-edge-to-edge and StatusBarShadow for visual treatment.

TouchableOpacity — React Native's touchable with opacity feedback. The application uses the custom Button component and Pressable instead.

Pressable — React Native's flexible touchable. Used internally by the custom Button component.

RefreshControl — React Native's pull-to-refresh control. Used inside List via the onRefresh and refreshing props.

Design System Terms

atoms (a) — The application's atomic style token system from #/alf. Provides pre-defined style objects (e.g., a.flex_1, a.px_xl, a.rounded_full) analogous to Tailwind CSS utility classes but for React Native StyleSheet.

useTheme (t) — A hook from #/alf returning the current theme object, including t.palette (raw color values), t.atoms (semantic style objects), and t.name ('light', 'dim', or 'dark').

dim — A third theme mode in addition to light and dark. Bluesky's "dim" mode is a softer dark theme.

platform() — A utility from #/alf that returns different values based on the current platform (native vs. web), similar to React Native's Platform.select().

web() — A utility from #/alf that applies styles only on the web platform, returning null or undefined on native.

native() — A utility from #/alf that applies styles or values only on native (iOS/Android) platforms.

gtMobile — A breakpoint boolean from useBreakpoints() that is true when the viewport is wider than a mobile screen (tablet/desktop).

gtPhone — A breakpoint boolean from useBreakpoints() that is true when the viewport is wider than a phone screen.

FeedDescriptor — A pipe-delimited string identifying a feed: "type|uriOrDid|tab". Examples: "following", "feedgen|at://...", "author|did:plc:...|posts_and_author_threads".

Shadow<T> — A type wrapper from #/state/cache/types representing a locally-cached or optimistically-updated version of a server-fetched object.

ModerationDecision — An @atproto/api object encoding content filtering decisions (blur, hide, warn) for a piece of content based on the user's moderation settings. The .ui(context) method returns display instructions for a specific UI context.

ModerationOpts — Configuration object passed to moderateProfile() containing the viewer's moderation preferences and label definitions.

Accessibility Terms

accessibilityRole — React Native prop that communicates the semantic role of a component to assistive technologies (e.g., "button", "image", "link", "header"). Equivalent to ARIA role on web.

accessibilityLabel — React Native prop providing the accessible name for a component, read aloud by screen readers. Equivalent to aria-label on web.

accessibilityHint — React Native prop providing additional context about a component's behavior, read after the label by screen readers. Equivalent to aria-describedby on web.

accessibilityState — React Native prop communicating the current state of a component (e.g., { selected: true }, { disabled: true }). Equivalent to aria-selected, aria-disabled on web.

accessibilityValue — React Native prop communicating a numeric or text value (e.g., for sliders or progress indicators). Equivalent to aria-valuenow on web.

accessibilityActions — React Native prop defining custom accessibility actions (e.g., magicTap, longpress) that can be triggered by assistive technologies.

accessibilityLiveRegion — React Native prop for dynamic content that should be announced without focus change. Equivalent to aria-live on web.

VoiceOver — Apple's screen reader for iOS and macOS. Activated by triple-clicking the side button on iPhone.

TalkBack — Google's screen reader for Android. Activated via the Accessibility settings menu.

onAccessibilityTap — React Native prop for handling the VoiceOver double-tap gesture on a component.

Accessibility actions — Named actions (e.g., activate, longpress, magicTap) that can be triggered by assistive technologies via accessibilityActions and onAccessibilityAction.

Technologies

React Native — A framework for building native mobile apps using React and JavaScript. Components render to native views, not HTML DOM elements.

Expo — A platform and set of tools built on top of React Native. The application uses Expo SDK for native modules (expo-image, expo-blur, expo-linear-gradient, expo-clipboard, expo-contacts, expo-updates).

react-native-gesture-handler — A library providing declarative gesture APIs (Gesture.Pan(), GestureDetector, TapGestureHandler). Used for swipe-to-action in GestureActionView.

react-native-reanimated — A library for high-performance animations running on the UI thread via worklets. Used for scroll-driven animations in GrowableBanner, GrowableAvatar, StatusBarShadow, and GestureActionView.

react-native-safe-area-context — Provides useSafeAreaInsets() for device-specific safe area inset values.

react-native-keyboard-controller — Provides KeyboardAwareScrollView for keyboard-aware scroll behavior.

react-native-webview — Provides the WebView component for embedding web content. Used in the Captcha step of the signup flow.

react-native-view-shot — Provides screenshot capture of React Native views. Used in PlaceholderCanvas to generate placeholder avatar images.

expo-image — Expo's optimized image component with caching, transitions, and prefetchAsync(). Used throughout the application in place of React Native's built-in Image.

expo-blur — Expo's BlurView component. Used in GrowableBanner for the overscroll blur effect on iOS.

expo-linear-gradient — Expo's LinearGradient component. Used in StatusBarShadow.

AT Protocol (atproto) — The open, decentralized social networking protocol underlying Bluesky. Defines lexicons (schemas) for posts, feeds, actors, labels, and more. All API calls use AT Protocol XRPC methods.

DID (Decentralized Identifier) — A globally unique, persistent identifier for a Bluesky user account (e.g., did:plc:abc123). Used as the canonical identity key throughout the codebase.

Handle — A human-readable username in the AT Protocol (e.g., alice.bsky.social). Handles can change; DIDs cannot.

AT URI — A URI scheme for AT Protocol resources: at://<did>/<collection>/<rkey>. Used to identify posts, feeds, lists, and other records.

XRPC — The remote procedure call protocol used by AT Protocol. Queries (reads) use GET; procedures (writes) use POST.

Lexicon — AT Protocol's schema definition system. Methods are namespaced (e.g., app.bsky.feed.searchPosts, com.atproto.server.createSession).

TanStack Query (React Query) — The data-fetching and server state management library used throughout the application. Provides useQuery, useInfiniteQuery, useMutation, and useQueryClient.

Lingui — The internationalization (i18n) library used in the application. Trans wraps JSX content for translation, msg marks string literals for extraction, and useLingui provides the _() translation function.

GestureHandlerRootView — Required root wrapper from react-native-gesture-handler that must wrap the entire application for gesture handling to work correctly.

SafeAreaProvider — Required root wrapper from react-native-safe-area-context that provides safe area inset values to all descendant components.

Worklet — A Reanimated concept — a JavaScript function annotated to run on the UI thread rather than the JS thread, enabling frame-synchronous animation updates without bridge overhead.

SharedValue — A Reanimated primitive that holds a value accessible on both the JS thread and the UI thread simultaneously, enabling smooth animations without JS bridge round-trips.

useAnimatedStyle — A Reanimated hook that creates a style object computed on the UI thread, re-evaluated whenever any SharedValue it reads changes.

runOnUI / runOnJS — Reanimated utilities for crossing the JS↔︎UI thread boundary. runOnUI schedules a worklet to run on the UI thread; runOnJS schedules a JS function to be called from a worklet.