Documentation Index
Fetch the complete documentation index at: https://mintlify.com/satsigner/satsigner/llms.txt
Use this file to discover all available pages before exploring further.
Overview
SatSigner’s component library is built with React Native and TypeScript, providing a consistent, reusable UI system across the mobile application. All components follow strict typing conventions and are documented using Storybook for interactive development and testing.
Component Architecture
Base Components
Components in SatSigner follow a consistent pattern with TypeScript prop types and style composition:
import { StyleSheet, TouchableOpacity } from 'react-native'
import { useMemo } from 'react'
export type SSButtonProps = {
label: string
variant?:
| 'default'
| 'secondary'
| 'outline'
| 'ghost'
| 'subtle'
| 'gradient'
| 'danger'
loading?: boolean
withSelect?: boolean
uppercase?: boolean
gradientType?: 'default' | 'special'
textStyle?: StyleProp<TextStyle>
} & React.ComponentPropsWithoutRef<typeof TouchableOpacity>
function SSButton({
label,
variant = 'default',
loading,
disabled,
style,
...props
}: SSButtonProps) {
const buttonStyle = useMemo(() => {
let buttonVariantStyles = styles.buttonDefault
if (variant === 'secondary') buttonVariantStyles = styles.buttonSecondary
if (variant === 'outline') buttonVariantStyles = styles.buttonOutline
// ... variant logic
return StyleSheet.compose(
{
...styles.buttonBase,
...(disabled ? styles.disabled : {}),
...buttonVariantStyles
},
style
)
}, [variant, disabled, style])
return (
<TouchableOpacity
style={buttonStyle}
activeOpacity={0.6}
disabled={disabled || loading}
{...props}
>
{/* Component content */}
</TouchableOpacity>
)
}
Type-Safe Props
All components use TypeScript interfaces extending base React Native component props:
export type SSTextProps = {
color?: 'white' | 'black' | 'muted'
size?: TextFontSize
type?: 'sans-serif' | 'mono'
weight?: TextFontWeight
uppercase?: boolean
center?: boolean
} & React.ComponentPropsWithoutRef<typeof Text>
Style Composition
Use useMemo for performance and StyleSheet.compose for style merging:
const textStyle = useMemo(() => {
const colorStyle = {
white: styles.textColorWhite,
black: styles.textColorBlack,
muted: styles.textColorMuted
}[color]
return StyleSheet.compose(
{
...styles.textBase,
...colorStyle,
fontSize: Sizes.text.fontSize[size],
...(uppercase ? styles.uppercase : {})
},
style
)
}, [color, size, uppercase, style])
Component Categories
- SSButton - Primary button with variants (default, secondary, outline, ghost, gradient, danger)
- SSTextInput - Text input with label and validation support
- SSCheckbox - Checkbox with label and state management
- SSRadioButton - Radio button for single selection
- SSSwitch - Toggle switch component
- SSSlider - Value slider with min/max bounds
- SSAmountInput - Bitcoin amount input with sat/BTC conversion
- SSFeeInput - Transaction fee input with rate calculation
Bitcoin-Specific Components
components/SSUtxoCard.tsx
import { useLocalSearchParams, useRouter } from 'expo-router'
import { TouchableOpacity } from 'react-native'
import { useShallow } from 'zustand/react/shallow'
type SSUtxoCardProps = {
utxo: Utxo
totalBalance?: number
}
function SSUtxoCard({ utxo, totalBalance }: SSUtxoCardProps) {
const [fiatCurrency, satsToFiat] = usePriceStore(
useShallow((state) => [state.fiatCurrency, state.satsToFiat])
)
const [currencyUnit, useZeroPadding] = useSettingsStore(
useShallow((state) => [state.currencyUnit, state.useZeroPadding])
)
const router = useRouter()
const { id } = useLocalSearchParams<AccountSearchParams>()
const { txid, vout } = utxo
return (
<TouchableOpacity
onPress={() =>
router.navigate(
`/signer/bitcoin/account/${id}/transaction/${txid}/utxo/${vout}`
)
}
>
{totalBalance !== undefined && totalBalance > 0 && (
<SSUtxoBar utxoValue={utxo.value} totalBalance={totalBalance} />
)}
{/* UTXO details display */}
</TouchableOpacity>
)
}
Data Display Components
- SSTransactionCard - Transaction list item with type, amount, timestamp
- SSUtxoCard - UTXO display with address and value
- SSAddressCard - Address display with QR code and copy
- SSAccountCard - Account overview with balance
- SSDetailsList - Key-value pairs for detail screens
Visualization Components
- SSBubbleChart - UTXO visualization as bubbles
- SSHistoryChart - Balance history over time
- SSTransactionChart - Transaction flow visualization
- SSFeeRateChart - Fee rate distribution
- SSSankeyDiagram - Transaction flow Sankey diagram
Storybook Integration
Configuration
Storybook is configured in .storybook/main.ts:
import type { StorybookConfig } from '@storybook/react-native'
const main: StorybookConfig = {
stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
addons: [
'@storybook/addon-ondevice-controls',
'@storybook/addon-ondevice-actions',
'@storybook/addon-ondevice-backgrounds',
'@storybook/addon-ondevice-notes'
]
}
export default main
Writing Stories
Create stories using the Component Story Format (CSF):
components/SSButton.stories.tsx
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'
import type { Meta, StoryObj } from '@storybook/react'
import { storybookBackgrounds } from '@/.storybook/utils/backgrounds'
import SSButton from './SSButton'
import storybookLayoutDecorator from './SSStoryBookLayout'
const meta = {
title: 'SSButton',
component: SSButton,
args: {
label: 'Satsigner',
variant: 'default',
gradientType: 'default'
},
argTypes: {
variant: {
control: 'select',
options: [
'default',
'secondary',
'outline',
'ghost',
'subtle',
'gradient',
'danger'
]
},
loading: {
control: 'boolean',
description: 'Button loading'
},
withSelect: {
control: 'boolean',
description: 'With select icon'
},
uppercase: {
control: 'boolean',
description: 'Text uppercase'
}
},
decorators: [storybookLayoutDecorator, withBackgrounds],
parameters: {
backgrounds: storybookBackgrounds
}
} satisfies Meta<typeof SSButton>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {}
export const Secondary: Story = {
args: {
variant: 'secondary'
}
}
export const Gradient: Story = {
args: {
variant: 'gradient'
},
argTypes: {
gradientType: {
control: 'select',
options: ['default', 'special']
}
}
}
Running Storybook
Run Storybook in development mode:
# iOS with Storybook enabled
yarn sb:ios
# Android with Storybook enabled
yarn sb:android
Storybook is enabled via the EXPO_PUBLIC_STORYBOOK_ENABLED environment variable.
State Management Integration
Components integrate with Zustand stores using the useShallow selector for performance:
import { useShallow } from 'zustand/react/shallow'
import { usePriceStore } from '@/store/price'
import { useSettingsStore } from '@/store/settings'
function MyComponent() {
// Select only needed values to prevent unnecessary re-renders
const [fiatCurrency, satsToFiat] = usePriceStore(
useShallow((state) => [state.fiatCurrency, state.satsToFiat])
)
const [currencyUnit, useZeroPadding] = useSettingsStore(
useShallow((state) => [state.currencyUnit, state.useZeroPadding])
)
// Component logic
}
Styling Best Practices
Theme System
Use centralized style constants from @/styles:
import { Colors, Sizes, Typography } from '@/styles'
const styles = StyleSheet.create({
buttonBase: {
borderRadius: Sizes.button.borderRadius,
height: Sizes.button.height,
width: '100%'
},
buttonDefault: {
backgroundColor: Colors.gray[600]
},
textDefault: {
color: Colors.white,
fontFamily: Typography.sfProTextRegular,
fontSize: Sizes.text.fontSize.sm
}
})
Responsive Design
Handle different screen sizes using layout components:
import SSHStack from '@/layouts/SSHStack'
import SSVStack from '@/layouts/SSVStack'
<SSVStack gap="md">
<SSHStack justifyBetween alignCenter>
<SSText>Label</SSText>
<SSText>Value</SSText>
</SSHStack>
</SSVStack>
Component Naming Convention
All SatSigner components are prefixed with SS:
- SS prefix identifies SatSigner custom components
- PascalCase for component names:
SSButton, SSTransactionCard
- File names match component names:
SSButton.tsx
- Story files end with
.stories.tsx: SSButton.stories.tsx
Testing Components
Components should be tested through:
- Storybook stories for visual testing and documentation
- Unit tests for logic and state management (see Testing)
- Integration tests for user interactions and navigation flows
Best Practices
- Type Everything - Use strict TypeScript types for all props
- Memoization - Use
useMemo and useCallback for expensive computations
- Shallow Selectors - Use
useShallow with Zustand to prevent unnecessary re-renders
- Style Composition - Use
StyleSheet.compose to merge styles efficiently
- Accessibility - Add proper accessibility props (accessibilityLabel, accessibilityRole)
- Error Boundaries - Wrap components that fetch data in error boundaries
- Loading States - Show loading indicators for async operations
- Responsive - Test components on different screen sizes
Component Documentation
Every component should include:
- JSDoc comments describing the component’s purpose
- Prop types with descriptions using TypeScript
- Storybook story showing common use cases
- Usage examples in documentation
/**
* SSButton - Primary button component with multiple variants
*
* @example
* <SSButton
* label="Send Bitcoin"
* variant="gradient"
* onPress={handleSend}
* />
*/
export type SSButtonProps = {
/** Button text label */
label: string
/** Visual style variant */
variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'gradient' | 'danger'
/** Show loading spinner */
loading?: boolean
}
Resources