API Reference
Complete reference for all portal methods, properties, and hooks.
Portal Creation
Factory Functions
import { modal, drawer, bottomSheet } from '@codeleap/portals'
// Create a modal
const MyModal = modal()
const MyModalWithId = modal({ id: 'MY_MODAL' })
const ConfiguredModal = modal({
id: 'CONFIGURED',
initialParams: { theme: 'dark' },
startsOpen: false,
independent: false,
rendersWhenHidden: false,
resetParamsOnClose: true,
transitionDuration: 300,
metadata: { category: 'user' }
})
// Create a drawer
const MyDrawer = drawer()
// Create a bottom sheet
const MyBottomSheet = bottomSheet()
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
id | string | auto-generated | Unique identifier for the portal |
initialParams | Params | () => Promise<Params> | {} | Initial parameters or async loader |
startsOpen | boolean | () => Promise<boolean> | false | Whether to open on mount |
independent | boolean | false | If true, doesn't participate in global stack |
rendersWhenHidden | boolean | false | If true, renders even when not visible |
resetParamsOnClose | boolean | true | Reset params to initial values on close |
transitionDuration | number | class default | Animation duration in milliseconds |
metadata | Metadata | {} | Additional metadata for the portal |
Core Methods
.content(render)
Defines the content render function. Required for the portal to display.
MyModal.content((props) => {
const { visible, toggle, close, open, setParams, request, ...params } = props
return (
<div>
<h2>Modal Content</h2>
<p>Visible: {visible ? 'Yes' : 'No'}</p>
<button onClick={toggle}>Toggle</button>
</div>
)
})
Parameters:
render:(props) => JSX.Element- Function that receives props and returns JSX
Returns: The portal instance (for chaining)
.props(props)
Sets wrapper component props. Can be static or dynamic.
// Static props
MyModal.props({
title: 'My Modal',
style: ['modal', 'elevated'],
showClose: true
})
// Dynamic props (based on params)
MyModal.props((params) => ({
title: `Editing ${params.itemName}`,
style: params.isImportant ? ['modal', 'urgent'] : ['modal']
}))
Parameters:
props:WrapperProps | (params: Params) => WrapperProps- Props object or function
Returns: The portal instance (for chaining)
.open(params?)
Opens the portal with optional parameters.
// Open without params
MyModal.open()
// Open with params
MyModal.open({
userId: '123',
mode: 'edit'
})
Parameters:
params?:Partial<Params>- Parameters to merge with current params
Returns: Promise<void>
.close()
Closes the portal and optionally resets parameters.
MyModal.close()
Returns: Promise<void>
.toggle()
Toggles portal visibility (opens if closed, closes if open).
MyModal.toggle()
Returns: Promise<void>
.request(params?, force?)
Opens the portal and returns a promise that resolves when the portal is closed with a result.
const result = await ConfirmModal.request({
message: 'Are you sure?'
})
if (result === true) {
console.log('User confirmed')
}
// Force new request even if one is pending
const result2 = await ConfirmModal.request({ message: 'Force' }, true)
Parameters:
params?:Partial<Params>- Parameters to pass to the portalforce?:boolean- Force new request even if one is pending (default:false)
Returns: Promise<Result>
.setParams(next)
Updates portal parameters without opening.
// Object merge
MyModal.setParams({ theme: 'dark' })
// Updater function
MyModal.setParams((prev) => ({
...prev,
count: prev.count + 1
}))
Parameters:
next:Partial<Params> | (prev: Params) => Partial<Params>- New params or updater
Returns: void
.resetParams()
Resets parameters to initial values.
MyModal.resetParams()
Returns: void
.getParams()
Gets current parameter values.
const params = MyModal.getParams()
console.log(params)
Returns: Params
.onOpen(callback)
Registers a callback to execute when the portal opens.
MyModal.onOpen((portal) => {
console.log('Modal opened!', portal.id)
console.log('Current params:', portal.getParams())
})
Parameters:
callback:(portal) => void- Function to execute on open
Returns: The portal instance (for chaining)
.onClose(callback)
Registers a callback to execute when the portal closes.
MyModal.onClose((portal) => {
console.log('Modal closed!', portal.id)
})
Parameters:
callback:(portal) => void- Function to execute on close
Returns: The portal instance (for chaining)
React Hooks
.useState()
React hook to access portal state (visibility and params).
function MyComponent() {
const { visible, params } = UserModal.useState()
return (
<div>
<p>Modal is {visible ? 'open' : 'closed'}</p>
<p>User: {params.username}</p>
<button onClick={UserModal.toggle}>
{visible ? 'Close' : 'Open'}
</button>
</div>
)
}
Returns:
visible:boolean- Current visibility stateparams:Params- Current parameters
.useProps(props, deps?)
React hook to update wrapper props dynamically with dependency tracking.
function MyComponent({ theme, title }) {
const [isLoading, setIsLoading] = useState(false)
MyModal.useProps({
title: isLoading ? 'Loading...' : title,
theme: theme,
disabled: isLoading
}, [theme, title, isLoading])
return <button onClick={MyModal.open}>Open</button>
}
Parameters:
props:WrapperProps- Props to merge with existing wrapper propsdeps?:React.DependencyList- Dependency array (optional, default:[])
Returns: void
Read-Only Properties
.isVisible
Current visibility state.
if (MyModal.isVisible) {
console.log('Modal is open')
}
Type: boolean
.currentParams
Current parameter values.
const params = MyModal.currentParams
console.log('Current user:', params.userId)
Type: Params
.stackIndex
Position in the portal stack (z-index).
const zIndex = MyModal.stackIndex
console.log('Portal z-index:', zIndex)
Type: number (returns -1 if not in stack)
.hasPendingRequest
Whether there's a pending request waiting for resolution.
if (MyModal.hasPendingRequest) {
console.log('Waiting for user response...')
}
Type: boolean
.id
Unique identifier for the portal.
console.log('Portal ID:', MyModal.id)
Type: string
.ref
React ref to the wrapper component.
// Access native methods (for BottomSheet)
MyBottomSheet.ref.current?.snapToIndex?.(0)
Type: React.RefObject<RefType>
Content Props
When you define content with .content(), the render function receives these props:
MyModal.content((props) => {
const {
// State
visible, // boolean - current visibility
params, // Params - current parameters (also destructured)
// Control methods
toggle, // () => Promise<void> - toggle visibility
close, // () => Promise<void> - close portal
open, // (params?) => Promise<void> - open portal
setParams, // (next) => void - update params
// Request system
request, // { resolve, reject } | null - if opened via .request()
// Deprecated (use toggle instead)
nextOrToggle, // () => Promise<void> - alias for toggle
previousOrToggle, // () => Promise<void> - alias for toggle
// Ref
ref, // React.RefObject<RefType> - wrapper ref
// Your custom parameters (destructured)
...yourParams // All params from .open() or .request()
} = props
return <div>Content</div>
})
Using Request Props
const ConfirmModal = modal()
.content((props) => {
const { request, message } = props
if (!request) {
return null // Not opened via .request()
}
return (
<div>
<p>{message}</p>
<button onClick={() => request.resolve(true)}>Yes</button>
<button onClick={() => request.reject('cancelled')}>No</button>
</div>
)
})
// Usage
try {
const confirmed = await ConfirmModal.request({ message: 'Continue?' })
if (confirmed) {
// User clicked Yes
}
} catch (error) {
// User clicked No
console.log('Cancelled:', error)
}
Global Outlet
Portal.GlobalOutlet()
Renders all non-independent portals. Place once in your app root.
import { Portal } from '@codeleap/portals'
function App() {
return (
<div>
<YourAppContent />
<Portal.GlobalOutlet />
</div>
)
}
Portal Registry
Access Portal Registry
import { Portal, Modal } from '@codeleap/portals'
// Access global registry
const allPortals = Portal.registry.getAll()
const userModal = Portal.registry.getInstance('USER_MODAL')
// Modal-specific registry
const modalInstance = Modal.registry.getInstance('MY_MODAL')
// Get stack index
const index = Portal.registry.getStackIndex('USER_MODAL')
// Filter portals
const openPortals = Portal.registry.filter(p => p.isVisible)
Registry Methods
| Method | Description |
|---|---|
register(id, instance) | Register a portal instance |
unregister(id) | Remove a portal from registry and stack |
getInstance(id) | Get portal instance by ID |
push(id) | Add portal to top of stack |
remove(id) | Remove portal from stack |
getStackIndex(id) | Get z-index position of portal |
getAll() | Get all registered portals |
filter(predicate) | Filter portals by condition |
clear() | Clear all portals |
Component Rendering
.Component(props?)
Renders the portal as a React component. Useful for independent portals.
const IndependentModal = modal({ independent: true })
.content(() => <div>Independent modal</div>)
function App() {
return (
<div>
{/* Render independent portal manually */}
<IndependentModal.Component />
{/* Other portals via outlet */}
<Portal.GlobalOutlet />
</div>
)
}
// Pass params and portalProps
<IndependentModal.Component
userId="123"
portalProps={{ title: 'User Modal' }}
/>