@54vie/kit - UI Component Library
UI Component Library for 54VIE Super App, built on top of React Native Paper.
Installation
pnpm add @54vie/kit
Quick Start
import { Button, Input, Card, Toast, ToastProvider } from '@54vie/kit';
function App() {
return (
<ToastProvider>
<Card>
<Input placeholder="Enter your name" />
<Button onPress={() => Toast.success('Saved!')}>
Save
</Button>
</Card>
</ToastProvider>
);
}
Components
Buttons
import { Button, IconButton, FAB } from '@54vie/kit';
// Variants
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
// Sizes
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>
// With icon
<Button icon="check">Confirm</Button>
// Loading state
<Button loading>Submitting...</Button>
// Icon Button
<IconButton icon="settings" onPress={openSettings} />
// Floating Action Button
<FAB icon="plus" onPress={addItem} />
Inputs
import { Input, SearchInput, OTPInput, PhoneInput } from '@54vie/kit';
// Basic input
<Input
label="Email"
placeholder="Enter email"
value={email}
onChangeText={setEmail}
error={errors.email}
/>
// Password input
<Input
label="Password"
secureTextEntry
value={password}
onChangeText={setPassword}
/>
// Search input
<SearchInput
placeholder="Search..."
value={query}
onChangeText={setQuery}
onSubmit={handleSearch}
/>
// OTP Input
<OTPInput
length={6}
value={otp}
onChange={setOtp}
autoFocus
/>
// Phone Input
<PhoneInput
value={phone}
onChangeText={setPhone}
defaultCountry="VN"
/>
Cards
import { Card, CardHeader, CardContent, CardFooter } from '@54vie/kit';
<Card>
<CardHeader
title="Product Name"
subtitle="$99.99"
avatar={{ uri: 'https://...' }}
/>
<CardContent>
<Text>Product description...</Text>
</CardContent>
<CardFooter>
<Button>Add to Cart</Button>
</CardFooter>
</Card>
Lists
import { List, ListItem, ListSection } from '@54vie/kit';
<List>
<ListSection title="Settings">
<ListItem
title="Notifications"
description="Manage notifications"
leftIcon="bell"
rightIcon="chevron-right"
onPress={() => {}}
/>
<ListItem
title="Privacy"
leftIcon="lock"
rightIcon="chevron-right"
onPress={() => {}}
/>
</ListSection>
</List>
Modals & Sheets
import { Modal, BottomSheet, ActionSheet } from '@54vie/kit';
// Modal
<Modal visible={visible} onDismiss={close}>
<Modal.Title>Confirm</Modal.Title>
<Modal.Content>Are you sure?</Modal.Content>
<Modal.Actions>
<Button variant="ghost" onPress={close}>Cancel</Button>
<Button onPress={confirm}>Confirm</Button>
</Modal.Actions>
</Modal>
// Bottom Sheet
<BottomSheet visible={visible} onDismiss={close}>
<BottomSheet.Header title="Options" />
<BottomSheet.Content>
{/* Content */}
</BottomSheet.Content>
</BottomSheet>
// Action Sheet
ActionSheet.show({
title: 'Choose action',
options: [
{ label: 'Edit', onPress: edit },
{ label: 'Delete', onPress: del, destructive: true },
],
cancelLabel: 'Cancel',
});
Toast
import { Toast, ToastProvider } from '@54vie/kit';
// Wrap app with provider
<ToastProvider position="bottom">
<App />
</ToastProvider>
// Show toasts
Toast.success('Saved successfully!');
Toast.error('Something went wrong');
Toast.info('New message received');
Toast.warning('Low battery');
// With options
Toast.show({
type: 'success',
message: 'Order placed!',
duration: 3000,
action: {
label: 'Undo',
onPress: () => undoOrder(),
},
});
Loading & Skeleton
import { Loading, Skeleton, FullScreenLoading } from '@54vie/kit';
// Spinner
<Loading size="large" color="primary" />
// Full screen loading
<FullScreenLoading message="Loading..." />
// Skeleton
<Skeleton width={200} height={20} />
<Skeleton.Circle size={50} />
<Skeleton.Card />
Hooks
useTheme
import { useTheme, useColorMode } from '@54vie/kit';
function MyComponent() {
const theme = useTheme();
const { colorMode, toggleColorMode } = useColorMode();
return (
<View style={{ backgroundColor: theme.colors.background }}>
<Text style={{ color: theme.colors.text }}>
Current mode: {colorMode}
</Text>
<Button onPress={toggleColorMode}>Toggle Theme</Button>
</View>
);
}
useResponsive
import { useResponsive } from '@54vie/kit';
function MyComponent() {
const { isMobile, isTablet, width, height } = useResponsive();
return (
<View style={{ flexDirection: isMobile ? 'column' : 'row' }}>
{/* Responsive layout */}
</View>
);
}
Theme Customization
import { ThemeProvider, createTheme } from '@54vie/kit';
const customTheme = createTheme({
colors: {
primary: '#FF6B00',
secondary: '#00B4D8',
},
fonts: {
regular: 'Inter-Regular',
medium: 'Inter-Medium',
bold: 'Inter-Bold',
},
roundness: 8,
});
function App() {
return (
<ThemeProvider theme={customTheme}>
<MainApp />
</ThemeProvider>
);
}
Spacing & Typography
import { spacing, typography } from '@54vie/kit';
const styles = StyleSheet.create({
container: {
padding: spacing.md, // 16
marginBottom: spacing.lg, // 24
},
title: {
...typography.h1,
},
body: {
...typography.body,
},
});
Icons
import { Icon } from '@54vie/kit';
<Icon name="home" size={24} color="primary" />
<Icon name="settings" size={20} />
<Icon name="chevron-right" />
Available icons are from react-native-vector-icons/MaterialCommunityIcons.
Best Practices
1. Always use ToastProvider
// Good
<ToastProvider>
<App />
</ToastProvider>
// Bad - Toast won't work
<App />
2. Use semantic variants
// Good - Clear intent
<Button variant="primary">Submit</Button>
<Button variant="ghost">Cancel</Button>
// Avoid inline styles for common patterns
<Button style={{ backgroundColor: 'blue' }}>Submit</Button>
3. Handle loading states
// Good
<Button loading={isSubmitting} disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</Button>