Positioning
Selkit ships a lightweight, zero-dependency dropdown positioner by default. When you need collision handling or robust placement inside tricky containers, opt into @selkit/floating — without giving up the zero-dependency core.
Default positioner
Out of the box the dropdown is positioned with a small built-in positioner:
- Aligns to the control's left edge and matches its width
- Flips above the control when there isn't enough room below
- Tracks scroll and resize
Set dropdownParent to teleport the dropdown out of clipping overflow / transform ancestors (commonly document.body):
createSelkitDom('#el', { options, dropdownParent: document.body })<SelkitSelect :options="options" :dropdown-parent="document.body" /><SelkitSelect options={options} dropdownParent={document.body} />The default positioner only flips vertically. It does not shift horizontally to avoid the viewport edge, and it can drift inside transform or fixed-header containers. For those cases, use @selkit/floating.
Advanced positioning with @selkit/floating
@selkit/floating wraps @floating-ui/dom to add flip (vertical), shift (horizontal collision avoidance) and size (viewport-aware max-height). Install it alongside your adapter:
pnpm add @selkit/floatingPass createFloatingPositioner as the positioner factory. The same factory works across all three adapters:
import { createSelkitDom } from '@selkit/dom'
import { createFloatingPositioner } from '@selkit/floating'
createSelkitDom('#el', {
options,
positioner: createFloatingPositioner,
})<script setup>
import { createFloatingPositioner } from '@selkit/floating'
</script>
<template>
<SelkitSelect :options="options" :positioner="createFloatingPositioner" />
</template>import { createFloatingPositioner } from '@selkit/floating'
<SelkitSelect options={options} positioner={createFloatingPositioner} />When a positioner is provided, the adapter delegates all dropdown positioning to it and stops applying its own position styles. Pair it with dropdownParent when you also need to escape an overflow: hidden ancestor.
Customizing placement and padding
createFloatingPositioner(reference, floating, opts) accepts placement, padding, gap and autoWidth. The adapter supplies autoWidth; wrap the factory in a closure to set the rest:
createSelkitDom('#el', {
options,
positioner: (trigger, dropdown, opts) =>
createFloatingPositioner(trigger, dropdown, {
...opts,
placement: 'top-start',
padding: 12,
}),
})<SelkitSelect
options={options}
positioner={(trigger, dropdown, opts) =>
createFloatingPositioner(trigger, dropdown, {
...opts,
placement: 'top-start',
padding: 12,
})
}
/>Low-level API
For custom renderers, @selkit/floating also exposes the framework-agnostic pieces used internally:
import { position, applyPosition } from '@selkit/floating'
// Compute placement without mutating the DOM
const result = await position(referenceEl, floatingEl, { placement: 'bottom-start' })
// → { x, y, placement, availableWidth, availableHeight }
// Apply a result to an element (position: fixed, width, data-placement)
applyPosition(floatingEl, referenceEl.getBoundingClientRect().width, result, {
autoWidth: false,
})@selkit/floating is the only part of Selkit with a runtime dependency. If you don't install it, @selkit/core and the adapters stay dependency-free.