DOCS · npm v0.3.0 · 49 source components
MIT · React Native

Expo UI 컴포넌트, 빠르게 시작하고 안전하게 확장하세요.

npm v0.3.0에 공개된 31개와 v0.4 소스 미리보기 18개를 함께 문서화합니다. 미공개 상세 페이지는 검색에서 제외하며, 토큰 기반 light·dark 테마와 device edge 유틸을 같은 타입 안전 API로 제공합니다.
01 · QUICK START

설치는 한 줄, 첫 컴포넌트는 몇 줄이면 충분합니다.

기본 light theme와 영문 문구가 내장되어 있어 일반 컴포넌트와 단일 Sheet·Dialog는 Provider 없이도 시작할 수 있습니다. 앱 브랜드·한국어 문구, Menu·Select·Popover·Tooltip 또는 중첩 Sheet·Dialog 순서가 필요하면 루트 UiProvider를 두세요.
Terminal
pnpm add @gj-kit/expo-ui
SaveButton.tsx
import { Button } from '@gj-kit/expo-ui'; export function SaveButton() { return ( <Button label="저장" onPress={() => console.log('saved')} /> ); }
i
지원 기준
React 18 이상 · React Native 0.79 이상 · Node.js 20 이상을 peer 및 engine 기준으로 지원합니다. Expo 전용 native module은 없습니다.
02 · FOUNDATION

Theme과 Provider가 하나의 설계 언어를 만듭니다.

createThemes는 shared 오버라이드 뒤에 light와 dark 오버라이드를 적용해 완성된 ThemePair를 만듭니다. 루트 UiProvider는 스킴·문구·아이콘과 Menu·Select·Popover·Tooltip·Sheet의 overlay 환경을 함께 제공하고, 중첩 Provider는 바깥 stack과 tooltip coordinator를 재사용합니다.
31
semantic color roles
7
typography roles
2
built-in schemes
0
direct dependencies
src/theme.ts
// src/theme.ts import { createThemes } from '@gj-kit/expo-ui/theme'; export const themes = createThemes({ shared: { radius: { sm: 10 } }, light: { colors: { primary: '#1769C2', primaryStrong: '#0E5CAD', }, }, dark: { colors: { primary: '#5C9EEA', primaryStrong: '#6BAAF0' }, }, });
app/_layout.tsx
import { UiProvider, koStrings } from '@gj-kit/expo-ui'; import { themes } from '../src/theme'; export default function RootLayout() { return ( <UiProvider theme={themes} strings={koStrings}> {/* Menu·Select·Popover·Tooltip·Sheet의 overlay 환경도 자동으로 제공됩니다. */} </UiProvider> ); }
Light와 dark를 함께
ThemePair는 두 스킴을 항상 완성된 상태로 제공합니다. 단일 Theme을 넘기면 해당 스킴으로 고정됩니다.
Aa
문구와 아이콘은 앱 소유
koStrings·enStrings와 RenderIcon 슬롯을 Provider 한 곳에서 주입합니다. UiProvider가 없는 독립 overlay tree에서만 OverlayProvider를 직접 둡니다.
03 · COMPONENTS

49개의 작은 조각, 일관된 하나의 시스템.

컴포넌트는 앱 구조를 대신 소유하지 않습니다. 토큰과 명확한 prop 계약을 제공하고, 화면 흐름과 도메인 조립은 앱에 남겨 둡니다.
49
source components
아래 목록은 현재 루트 엔트리에서 실제 export되는 시각 컴포넌트 전체입니다.
Foundation
의미 기반 서체와 색 역할로 화면의 목소리를 맞춥니다.
Data
행·열 데이터의 의미, 정렬 요청과 선택 상태를 플랫폼에 맞게 표현합니다.
ReadingControls.tsx
import { useState } from 'react'; import { Select, Slider, ToggleGroup } from '@gj-kit/expo-ui'; const densityItems = [ { label: '여유', value: 'spacious' }, { label: '기본', value: 'comfortable' }, { label: '압축', value: 'compact' }, ] as const; const channelItems = [ { label: 'Stable', value: 'stable' }, { label: 'Preview', value: 'preview' }, ] as const; export function ReadingControls() { const [fontSize, setFontSize] = useState(16); const [density, setDensity] = useState<'spacious' | 'comfortable' | 'compact'>('comfortable'); const [channel, setChannel] = useState<'stable' | 'preview' | null>('stable'); const [selectOpen, setSelectOpen] = useState(false); return ( <> <Slider value={fontSize} min={12} max={24} step={1} accessibilityLabel="본문 글자 크기" onValueChange={setFontSize} /> <ToggleGroup selectionMode="single" value={density} onValueChange={(next) => next && setDensity(next)} accessibilityLabel="목록 밀도" items={densityItems} allowEmpty={false} /> <Select label="릴리스 채널" placeholder="채널 선택" items={channelItems} value={channel} onValueChange={setChannel} open={selectOpen} onOpenChange={(next) => setSelectOpen(next)} /> </> ); }
플랫폼에 맞는 overlay 의미
Menu는 웹에서 menuitem·checkbox focus와 typeahead를, Select는 포커스를 trigger에 유지하는 combobox·listbox를 제공합니다. Popover는 owned trigger에서 웹 non-modal rich dialog와 네이티브 adaptive Dialog로, Tooltip은 owned icon action에서 웹 시각 설명과 네이티브 accessibilityHint로 적응합니다. Sheet는 작은 화면의 bottom surface와 넓은 화면의 logical side panel을 같은 controlled 계약으로 연결합니다. public Portal·Host·asChild, submenu, 검색·다중 Select와 drag·snap BottomSheet adapter는 아직 계약하지 않습니다.
04 · DEVICE EDGES

Safe area와 키보드도 조합 가능한 유틸로.

/insets 엔트리는 하단 safe-area와 Android edge-to-edge Modal의 키보드 겹침을 다룹니다. 훅을 사용할 때만 react-native-safe-area-context가 optional peer로 필요합니다.
Optional peer for Expo
npx expo install react-native-safe-area-context
BottomBar.tsx
import { Button, StickyActionBar } from '@gj-kit/expo-ui'; import { useBottomInset } from '@gj-kit/expo-ui/insets'; export function BottomBar() { return ( <StickyActionBar bottomInset={useBottomInset()}> <Button label="완료" onPress={() => {}} /> </StickyActionBar> ); }
Keyboard utilities
import { computeKeyboardRevealOffset, useBottomSheetPadding, useModalKeyboardOverlap, } from '@gj-kit/expo-ui/insets'; const keyboardOverlap = useModalKeyboardOverlap(); const bottomPadding = useBottomSheetPadding(24);
useBottomInset
웹에서는 0, native에서는 실측 하단 inset을 반환합니다.
useBottomSheetPadding
디자인 여백과 실제 inset을 더합니다.
useModalKeyboardOverlap
별도 Modal 윈도우의 실제 키보드 가림 높이를 계산합니다.
computeKeyboardRevealOffset
포커스 입력의 아래쪽을 드러낼 스크롤 위치를 계산합니다.
nativeBottomInset / Padding
React 훅 없이 사용할 수 있는 순수 계산 함수입니다.
05 · NATIVEWIND

Tailwind는 선택하고, 토큰은 공유하세요.

스타일링 가능한 프리미티브는 style과 className 확장 지점을 제공하며 NativeWind 자체에는 의존하지 않습니다. /tailwind 엔트리는 Theme에서 preset을 만들어 런타임 테마와 클래스 토큰의 출처를 맞춥니다.
tailwind.preset.ts
import { createTailwindPreset } from '@gj-kit/expo-ui/tailwind'; import { themes } from './src/theme'; export const preset = createTailwindPreset(themes.light); // presets: [preset] // bg-ui-primary · p-ui-lg · rounded-ui-pill // text-ui-title · shadow-ui-sm · tablet: / desktop:
Node 컨텍스트에서 안전
/theme과 /tailwind 엔트리는 React와 React Native를 import하지 않아 설정 파일에서 바로 불러올 수 있습니다.
ui
기본 prefix는 ui
색, spacing, radius, 6개 일반 typography role, elevation과 breakpoint가 유틸리티로 파생됩니다.
!
다크 클래스의 정본
preset은 전달한 단일 Theme에서 만들어집니다. 런타임 스킴 전환은 useTheme이 담당하고, className의 dark: 전환은 NativeWind 설정에서 앱이 관리합니다.
06 · TYPE-SAFE BY DESIGN

동작하지 않는 UI를 만들기 어렵게.

접근성 라벨 누락, 내용 없는 버튼, 오타 난 토큰 키, 핸들러 없는 액션을 런타임까지 보내지 않습니다. 공개 타입 테스트가 정상 사용과 잘못된 사용을 함께 고정합니다.
534
unit tests
91
type tests
49
source components
Contracts.tsx
// TypeScript error: 접근성 라벨 누락 <IconButton icon={gear} onPress={openSettings} />; // OK <IconButton accessibilityLabel="설정 열기" icon={gear} onPress={openSettings} />; // TypeScript error: 동작 없는 액션 <EmptyState action={{ label: '추가' }} />; // OK <EmptyState action={{ label: '추가', onPress: create }} />;
Theme brand
createTheme/createThemes를 거치지 않은 손조립 테마를 거부합니다.
01
Accessible icon
IconButton의 accessibilityLabel은 필수입니다.
02
Button content
label과 children이 모두 없는 Button은 컴파일되지 않습니다.
03
Typed tabs
items에 없는 Tabs value 오타를 NoInfer로 차단합니다.
04
Complete strings
부분 번들 대신 완성된 UiStrings를 요구합니다.
05
Token keys
존재하지 않는 spacing·color 키를 사용 지점에서 거부합니다.
06
No legacy escape
이관 중 남은 unstyled prop은 스프레드 경유까지 거부합니다.
07
Explicit field style
TextField는 containerStyle과 inputStyle을 구분합니다.
08
Live actions only
EmptyState action은 label과 onPress를 함께 요구합니다.
09
Semantic text color
Text color는 토큰 키만 받고 raw 색은 명시적인 style로만 허용합니다.
10
Labeled controls
Checkbox와 Switch는 보이는 label 또는 accessibilityLabel을 요구합니다.
11
Progress modes
ProgressBar는 number와 null 상태를 분리하고 접근성 라벨을 필수로 받습니다.
12
현재 소스에서 직접 검증했습니다
unit 테스트 534개와 type-contract 테스트 91개, 총 625개가 통과하며 README의 TypeScript/TSX 예제도 배포 타입 선언을 기준으로 컴파일됩니다.
@gj-kit/expo-ui
Type-safe primitives for Expo, React Native and React Native Web.