Portal Types
@codeleap/portals provides different types of portals for common UI patterns. Each type extends the base Portal class with specific configurations and behaviors.
Modal
Centered overlay dialogs for important messages or confirmations.
Creating a Modal
import { modal } from '@codeleap/portals'
const MyModal = modal()
.content((props) => {
const { toggle } = props
return (
<div>
<h2>Modal Title</h2>
<p>Modal content goes here</p>
<button onClick={toggle}>Close</button>
</div>
)
})
MyModal.open()
With ID
const UserModal = modal({ id: 'USER_MODAL' })
.content(() => <div>User modal</div>)
// Access from anywhere
import { Modal } from '@codeleap/portals'
const instance = Modal.registry.getInstance('USER_MODAL')
instance.open()
Configuration
const ConfiguredModal = modal({
id: 'CONFIGURED_MODAL',
initialParams: { theme: 'dark' },
startsOpen: false,
independent: false,
rendersWhenHidden: false,
resetParamsOnClose: true,
transitionDuration: 300,
metadata: { category: 'user-actions' }
})
Drawer
Side-sliding panels for navigation, filters, or secondary content.
Use drawer as the default side-panel pattern for web.
Creating a Drawer
import { drawer } from '@codeleap/portals'
const SideDrawer = drawer()
.content((props) => {
const { close } = props
return (
<div>
<h2>Navigation</h2>
<nav>
<a href="/home">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<button onClick={close}>Close</button>
</div>
)
})
SideDrawer.open()
Default Configuration
- DEFAULT_TRANSITION_DURATION: 200ms
- Can be customized:
Drawer.DEFAULT_TRANSITION_DURATION = 250
BottomSheet
Bottom-sliding panels popular in mobile interfaces.
Use bottomSheet as the default bottom-panel pattern for mobile.
Creating a BottomSheet
import { bottomSheet } from '@codeleap/portals'
const FilterSheet = bottomSheet()
.content((props) => {
const { close, setParams } = props
return (
<div>
<h2>Filters</h2>
<label>
<input type="checkbox" />
Option 1
</label>
<button onClick={close}>Apply</button>
</div>
)
})
FilterSheet.open()
Default Configuration
- DEFAULT_TRANSITION_DURATION: 200ms
- rendersWhenHidden: true (always rendered for smooth animations)
- resetParamsOnClose: false (preserves state between opens)
Native Methods Integration
BottomSheet can integrate with native bottom sheet libraries (like @gorhom/bottom-sheet):
import { BottomSheet } from '@codeleap/portals'
// Configure methods to call on ref
BottomSheet.openKeyMethod = 'snapToIndex'
BottomSheet.closeKeyMethod = 'close'
// Now when you call .open() or .close(), it will also call
// ref.current.snapToIndex() or ref.current.close()
Lifecycle Hooks
const MySheet = bottomSheet()
.onOpen((portal) => {
console.log('Sheet opened!', portal.ref.current)
})
.onClose((portal) => {
console.log('Sheet closed!', portal.ref.current)
})
.content(() => <div>Content</div>)
Alert
Utility class for showing typed alert modals with convenient methods.
Use alert for simple prompt-style content such as:
- warnings
- info messages
- error prompts
- quick yes/no or ask flows
If the content becomes richer, more stateful, or more interactive, prefer modal() instead of forcing the flow through alert.
Setup
import { Alert, modal } from '@codeleap/portals'
// Create and assign the alert modal
Alert.modal = modal({ id: 'ALERT_MODAL' })
.content((props) => {
const { title, body, type, options = [], request } = props
return (
<div className={`alert alert--${type}`}>
<h2>{title}</h2>
<p>{body}</p>
<div className="alert-buttons">
{options.map((option, index) => (
<button
key={index}
className={`button button--${option.style}`}
onClick={() => {
option.onPress?.()
request?.resolve(option)
}}
>
{option.text}
</button>
))}
</div>
</div>
)
})
Using Alerts
import { alert } from '@codeleap/portals'
// Ask
alert.ask({
title: 'Quick question',
body: 'Do you want to continue?',
options: [
{ text: 'Yes', onPress: () => console.log('Yes') },
{ text: 'No', onPress: () => console.log('No') }
]
})
// Error
alert.error({
title: 'Whoops!',
body: 'Something went wrong',
options: [{ text: 'OK' }]
})
// Warning
alert.warn({
title: 'Hang on',
body: 'Are you sure you want to delete this?',
options: [
{ text: 'Delete', style: 'destructive' },
{ text: 'Cancel', style: 'cancel' }
]
})
// Info
alert.info({
title: 'Information',
body: 'Your changes have been saved',
options: [{ text: 'Got it' }]
})
// Custom
alert.custom({
title: 'Custom Alert',
body: 'With custom type',
customData: { foo: 'bar' },
options: [{ text: 'Close' }]
})
Custom Portal Type
You can create your own portal type by extending the Portal class:
import { Portal } from '@codeleap/portals'
export class CustomPortal extends Portal {
displayName = 'CustomPortal'
static WrapperComponent = null
static DEFAULT_TRANSITION_DURATION = 300
protected get defaultTransitionDuration() {
return CustomPortal.DEFAULT_TRANSITION_DURATION
}
protected get wrapperComponent() {
return CustomPortal.WrapperComponent
}
protected getDefaultConfig() {
return {
rendersWhenHidden: true,
independent: false,
}
}
}
export function customPortal(idOrConfig) {
return new CustomPortal(idOrConfig)
}