| name | create-package |
| description | Create or modify gluestack-core and gluestack-utils packages for component creators and utilities |
Create Package Workflow
Guide for creating or modifying packages in the gluestack-ui ecosystem. This includes working with gluestack-core (component creators) and gluestack-utils (utilities, hooks, styling helpers).
Package Types
gluestack-core
Contains component creators and core logic:
- Creator functions (factory pattern for components)
- ARIA hooks for accessibility
- Component-specific logic
- State management
Critical Export Requirements:
- All components need barrel exports in
src/[component]/index.tsx
- Main index must export all components:
src/index.tsx
- Deep imports enabled via
scripts/generate-barrel-exports.js (auto-runs on build)
- TypesVersions in package.json for TypeScript deep import support
gluestack-utils
Contains shared utilities:
- NativeWind utilities (tva, withStyleContext, useStyleContext)
- Common hooks
- Helper functions
- ARIA utilities from react-aria
Critical Export Requirements:
- All utilities need barrel exports
- Proper package.json exports configuration
Workflow
PHASE 1: Package Type Selection
Step 1.1: Determine Package Type
Ask the user:
questions:
- question: "Which package are you working on?"
header: "Package Type"
multiSelect: false
options:
- label: "gluestack-core"
description: "Component creators, ARIA hooks, component-specific logic"
- label: "gluestack-utils"
description: "Shared utilities, common hooks, styling helpers"
Step 1.2: Determine Task Type
Ask the user:
questions:
- question: "What do you want to do?"
header: "Task"
multiSelect: false
options:
- label: "Create new component creator"
description: "Add factory function for new component in gluestack-core"
- label: "Create ARIA hook"
description: "Add accessibility hook in gluestack-core"
- label: "Create utility function"
description: "Add helper function in gluestack-utils"
- label: "Modify existing package code"
description: "Update or fix existing package code"
PHASE 2: Analysis & Planning
Step 2.1: Analyze Existing Package Structure
Read relevant package files:
For gluestack-core:
packages/gluestack-core/
├── src/
│ ├── accordion/
│ │ ├── creator/index.tsx
│ │ ├── aria/index.tsx
│ │ └── index.tsx
│ ├── button/
│ ├── [component-name]/
│ └── index.tsx
├── package.json
└── tsconfig.json
For gluestack-utils:
packages/gluestack-utils/
├── src/
│ ├── nativewind-utils/
│ │ ├── tva/
│ │ ├── withStyleContext/
│ │ └── useStyleContext/
│ ├── hooks/
│ ├── aria/
│ └── common/
├── package.json
└── tsconfig.json
Step 2.2: Read Similar Implementations
Use Read tool to understand patterns:
For component creator:
- Read
packages/gluestack-core/src/accordion/creator/index.tsx
- Read
packages/gluestack-core/src/button/creator/index.tsx
- Understand factory pattern
For ARIA hook:
- Read
packages/gluestack-core/src/accordion/aria/index.tsx
- Understand react-aria integration
For utility:
- Read
packages/gluestack-utils/src/nativewind-utils/tva/index.tsx
- Understand utility patterns
PHASE 3: API Design
Step 3.1: Design Package API
Based on the task type:
Component Creator API:
export function create[ComponentName]<
RootProps,
ItemProps,
>({
Root,
Item,
}: {
Root: React.ComponentType<RootProps>;
Item: React.ComponentType<ItemProps>;
}) {
const [ComponentName] = [ComponentName]Main(Root) as any;
[ComponentName].Item = [ComponentName]Item(Item);
return [ComponentName] as I[ComponentName]ComponentType<
RootProps,
ItemProps,
>;
}
ARIA Hook API:
export function use[ComponentName]({
isDisabled,
isOpen,
onOpenChange,
}: Use[ComponentName]Props) {
const state = useOverlayTriggerState({ isOpen, onOpenChange });
const { triggerProps, overlayProps } = useOverlayTrigger({}, state, triggerRef);
return {
state,
triggerProps,
overlayProps,
};
}
Utility Function API:
export function utilityName(
input: InputType,
options?: OptionsType
): ReturnType {
return result;
}
Step 3.2: Present API to User
Show the proposed API and ask:
- Is the API clear and intuitive?
- Do the parameter names make sense?
- Is the return type appropriate?
- Any edge cases to consider?
CHECKPOINT 1: Get user approval on API design
PHASE 4: Implementation Plan
Step 4.1: File Structure Plan
For Component Creator (gluestack-core):
## Implementation Plan
### Files to Create:
packages/gluestack-core/src/[component-name]/
├── creator/
│ └── index.tsx # create[ComponentName] factory
├── aria/
│ └── index.tsx # use[ComponentName] hook (if needed)
└── index.tsx # Barrel export
### Updates Required:
- packages/gluestack-core/src/index.tsx (add export)
- packages/gluestack-core/package.json (if adding dependencies)
### Dependencies:
- react
- react-native
- @react-aria/[module] (if using react-aria)
- @react-stately/[module] (if using react-stately)
For Utility (gluestack-utils):
## Implementation Plan
### Files to Create:
packages/gluestack-utils/src/[category]/[utility-name]/
├── index.tsx # Main utility
├── types.ts # TypeScript types (if complex)
└── README.md # Usage documentation (optional)
### Updates Required:
- packages/gluestack-utils/src/index.tsx (add export)
- packages/gluestack-utils/package.json (if adding dependencies)
Step 4.2: Confirm Plan
CHECKPOINT 2: Get user confirmation on implementation plan
PHASE 5: Local Development Setup
Step 5.1: Link Packages for Development
yarn link:create-core
yarn link:create-utils
yarn link:apps
Available apps (all under apps/):
kitchen-sink — component showcase
starter-kit-expo — Expo + NativeWind v5 (Tailwind v4)
starter-kit-expo-uniwind — Expo + UniWind (Tailwind v4)
starter-kit-next — Next.js + NativeWind v4 (Tailwind v3)
starter-kit-monorepo — Universal Expo + Next.js monorepo (NativeWind v4, Tailwind v3)
website — documentation site
Step 5.2: Start Watch Mode
The package will now rebuild automatically on changes:
PHASE 6: Implementation
Step 6.1: Create Component Creator (if gluestack-core)
Example: packages/gluestack-core/src/dropdown/creator/index.tsx
'use client';
import React from 'react';
const DropdownMain = <T,>(StyledRoot: React.ComponentType<T>) => {
return React.forwardRef<T, any>(
({ children, ...props }, ref) => {
return (
<StyledRoot ref={ref} {...props}>
{children}
</StyledRoot>
);
}
);
};
const DropdownTrigger = <T,>(StyledTrigger: React.ComponentType<T>) => {
return React.forwardRef<T, any>(
({ children, ...props }, ref) => {
return (
<StyledTrigger ref={ref} {...props}>
{children}
</StyledTrigger>
);
}
);
};
export function createDropdown<
RootProps,
TriggerProps,
ContentProps,
>({
Root,
Trigger,
Content,
}: {
Root: React.ComponentType<RootProps>;
Trigger: React.ComponentType<TriggerProps>;
Content: React.ComponentType<ContentProps>;
}) {
const Dropdown = DropdownMain(Root) as any;
Dropdown.Trigger = DropdownTrigger(Trigger);
Dropdown.Content = DropdownContent(Content);
Dropdown.displayName = 'Dropdown';
return Dropdown as IDropdownComponentType<
RootProps,
TriggerProps,
ContentProps
>;
}
export type IDropdownComponentType<
RootProps,
TriggerProps,
ContentProps
> = React.ForwardRefExoticComponent<RootProps> & {
Trigger: React.ForwardRefExoticComponent<TriggerProps>;
Content: React.ForwardRefExoticComponent<ContentProps>;
};
Step 6.2: Create ARIA Hook (if needed)
Example: packages/gluestack-core/src/dropdown/aria/index.tsx
import { useOverlayTriggerState } from '@react-stately/overlays';
import { useOverlayTrigger } from '@react-aria/overlays';
import { useRef } from 'react';
export interface UseDropdownProps {
isOpen?: boolean;
defaultOpen?: boolean;
onOpenChange?: (isOpen: boolean) => void;
isDisabled?: boolean;
}
export function useDropdown(props: UseDropdownProps) {
const {
isOpen,
defaultOpen,
onOpenChange,
isDisabled = false,
} = props;
const triggerRef = useRef(null);
const state = useOverlayTriggerState({
isOpen,
defaultOpen,
onOpenChange,
});
const { triggerProps, overlayProps } = useOverlayTrigger(
{ type: 'dialog' },
state,
triggerRef
);
return {
state,
triggerRef,
triggerProps: {
...triggerProps,
'aria-disabled': isDisabled,
},
overlayProps,
};
}
Step 6.3: Create Utility Function (if gluestack-utils)
Example: packages/gluestack-utils/src/hooks/useControllableState/index.tsx
import { useCallback, useState, useRef, useEffect } from 'react';
export interface UseControllableStateProps<T> {
value?: T;
defaultValue?: T;
onChange?: (value: T) => void;
}
export function useControllableState<T>({
value: controlledValue,
defaultValue,
onChange,
}: UseControllableStateProps<T>) {
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
const isControlled = controlledValue !== undefined;
const value = isControlled ? controlledValue : uncontrolledValue;
const setValue = useCallback(
(nextValue: T | ((prev: T) => T)) => {
const newValue =
typeof nextValue === 'function'
? (nextValue as (prev: T) => T)(value as T)
: nextValue;
if (!isControlled) {
setUncontrolledValue(newValue);
}
onChange?.(newValue);
},
[isControlled, onChange, value]
);
return [value, setValue] as const;
}
Step 6.4: Add TypeScript Types
Create proper TypeScript types for all exports:
export interface DropdownProps {
isOpen?: boolean;
onOpenChange?: (isOpen: boolean) => void;
children: React.ReactNode;
}
export interface DropdownTriggerProps {
children: React.ReactNode;
isDisabled?: boolean;
}
Step 6.5: Create Barrel Exports
CRITICAL: Proper Export Configuration
- Component Barrel Export -
packages/gluestack-core/src/dropdown/index.tsx:
export * from './creator';
export * from './creator/types';
export * from './aria';
- Main Index Export - Add to
packages/gluestack-core/src/index.tsx:
export * from './dropdown';
-
Deep Import Files - Automatically generated by scripts/generate-barrel-exports.js:
- The script runs during
yarn build
- It creates files like
packages/gluestack-core/dropdown/creator.ts
- These files export from
lib/esm/dropdown/creator
- Enables imports like:
import { createDropdown } from '@gluestack-ui/core/dropdown/creator'
-
TypeScript Support - TypesVersions in package.json:
- Manually add entries to typesVersions for new components
- Pattern:
"[component]/creator": ["./lib/esm/[component]/creator/index.d.ts"]
- This allows TypeScript to resolve deep imports correctly
PHASE 7: Testing
Step 7.1: Test in UI Component
Create or update a UI component that uses the package:
import { createDropdown, useDropdown } from '@gluestack-ui/dropdown';
import { View, Pressable } from 'react-native';
const UIDropdown = createDropdown({
Root: View,
Trigger: Pressable,
Content: View,
});
const Dropdown = React.forwardRef((props, ref) => {
const { state, triggerProps, overlayProps } = useDropdown(props);
return (
<UIDropdown ref={ref}>
{/* Component implementation */}
</UIDropdown>
);
});
Step 7.2: Test in Apps
Choose the relevant app(s) to test in based on what was changed:
cd apps/kitchen-sink && yarn start
cd apps/website && yarn dev
cd apps/starter-kit-expo && npx expo start
cd apps/starter-kit-expo-uniwind && npx expo start
cd apps/starter-kit-next && yarn dev
cd apps/starter-kit-monorepo && yarn dev
Which app to test in:
- New component creator → kitchen-sink + all starter kits
- Utility/hook → kitchen-sink is sufficient
- Styling change → all three starter kits (expo, expo-uniwind, next)
- Core breaking change → all apps
Verify:
- Package imports correctly in each tested app
- Functions work as expected
- TypeScript types are correct
- No console errors
- Documentation examples work
- Styling renders correctly across engines (NativeWind v5, UniWind, NativeWind v4)
Step 7.3: Build Test
cd packages/gluestack-core && yarn build
cd packages/gluestack-utils && yarn build
PHASE 8: Documentation
Step 8.1: Add JSDoc Comments
Add comprehensive JSDoc comments:
export function createDropdown({ ... }) {
}
Step 8.2: Update Package README (if major addition)
Update package README if adding significant functionality.
PHASE 9: Cleanup & Finalization
Step 9.1: Unlink Packages
yarn unlink:apps
Step 9.2: Final Checklist
## Package Development Checklist
### Code Quality
- [ ] TypeScript types complete
- [ ] JSDoc comments added
- [ ] No console.log statements
- [ ] Proper error handling
- [ ] Code is clean and readable
### Functionality
- [ ] Package exports correctly
- [ ] Functions work as expected
- [ ] TypeScript types correct
- [ ] No runtime errors
- [ ] Tested in UI components
### Build & Exports
- [ ] Package builds successfully
- [ ] No build warnings
- [ ] .d.ts files generated correctly
- [ ] Barrel exports created:
- [ ] Component barrel: src/[component]/index.tsx
- [ ] Main index updated: src/index.tsx
- [ ] Deep import files auto-generated in build
- [ ] TypesVersions updated in package.json (if needed)
- [ ] Deep imports work: @gluestack-ui/core/[component]/creator
### Documentation
- [ ] JSDoc comments complete
- [ ] Usage examples provided
- [ ] README updated (if needed)
### Testing
- [ ] Tested in apps
- [ ] Integration tested
- [ ] Edge cases considered
CHECKPOINT 3: Final confirmation
PHASE 10: Summary
## ✅ Package Development Complete!
### What Was Created:
- Package: packages/[package-name]/src/[component-or-utility]/
- Exports: Updated barrel exports
- Types: Complete TypeScript definitions
### Testing:
- ✅ Builds successfully
- ✅ TypeScript types correct
- ✅ Tested in UI components
- ✅ Works in apps
### Next Steps:
1. Review changes
2. Commit: `git commit -m "feat([package]): [description]"`
3. Create PR
4. After merge, publish package (maintainers only)
Ready to commit?
Best Practices
For Component Creators (gluestack-core)
- Follow factory pattern - Accept base components, return compound component
- Use forwardRef - Always forward refs to base components
- Proper TypeScript generics - Support any base component types
- displayName - Set for better debugging
- Consistent naming - Component + SubComponent pattern
For ARIA Hooks (gluestack-core)
- Use react-aria - Leverage existing ARIA implementations
- Return props objects - Return props to spread on elements
- State management - Use react-stately for complex state
- Composable - Hooks should work together
- Document behavior - Clear JSDoc for ARIA patterns
For Utilities (gluestack-utils)
- Pure functions - No side effects when possible
- Type safety - Proper TypeScript types
- Tree-shakeable - Export named exports
- Well documented - Clear JSDoc with examples
- Tested - Ensure utilities work correctly
Let's build great packages! 📦