Skip to main content

@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>