<script lang="ts">
import { SkullIcon } from '@lucide/svelte';
import { Portal } from '@skeletonlabs/skeleton-svelte';
let disabled = $state(true);
let target: HTMLElement | undefined = $state();
const cardClasses = 'card preset-outlined-surface-300-700 size-24 grid place-items-center p-4';
const buttonClasses = 'col-span-2 btn preset-filled';
</script>
<div class="grid grid-cols-2 gap-4">
<!-- Source -->
<div class={cardClasses}>
<Portal {disabled} {target}>
<SkullIcon class="size-8" />
</Portal>
</div>
<!-- Target -->
<div class={cardClasses} bind:this={target}>
<!-- (the content will appear here) -->
</div>
<!-- Trigger -->
<button class={buttonClasses} onclick={() => (disabled = !disabled)}>
{disabled ? 'Enable' : 'Disable'}
</button>
</div>
import { Portal } from '@skeletonlabs/skeleton-react';
import { SkullIcon } from 'lucide-react';
import { useState, useRef } from 'react';
export default function Default() {
const [disabled, setDisabled] = useState(true);
const ref = useRef<HTMLDivElement | null>(null);
const cardClasses = 'card preset-outlined-surface-300-700 size-24 grid place-items-center p-4';
const buttonClasses = 'col-span-2 btn preset-filled';
return (
<div className="grid grid-cols-2 gap-4">
{/* Source */}
<div className={cardClasses}>
<Portal disabled={disabled} target={ref.current ?? undefined}>
<SkullIcon className="size-8" />
</Portal>
</div>
{/* Target */}
<div className={cardClasses} ref={ref}>
{/* (the content will appear here) */}
</div>
{/* Trigger */}
<button className={buttonClasses} onClick={() => setDisabled(!disabled)}>
{disabled ? 'Enable' : 'Disable'}
</button>
</div>
);
}
Basic
Portals move rendered content from its original location in the DOM to a target location. This is helpful for overlays, modals, and tooltips where you need to escape parent styling or stacking contexts.
<script lang="ts">
import { SkullIcon } from '@lucide/svelte';
import { Portal } from '@skeletonlabs/skeleton-svelte';
let disabled = $state(true);
let target: HTMLElement | undefined = $state();
const cardClasses = 'card preset-outlined-surface-300-700 size-24 grid place-items-center p-4';
const buttonClasses = 'col-span-2 btn preset-filled';
</script>
<div class="grid grid-cols-2 gap-4">
<!-- Source -->
<div class={cardClasses}>
<Portal {disabled} {target}>
<SkullIcon class="size-8" />
</Portal>
</div>
<!-- Target -->
<div class={cardClasses} bind:this={target}>
<!-- (the content will appear here) -->
</div>
<!-- Trigger -->
<button class={buttonClasses} onclick={() => (disabled = !disabled)}>
{disabled ? 'Enable' : 'Disable'}
</button>
</div>
import { Portal } from '@skeletonlabs/skeleton-react';
import { SkullIcon } from 'lucide-react';
import { useState, useRef } from 'react';
export default function Default() {
const [disabled, setDisabled] = useState(true);
const ref = useRef<HTMLDivElement | null>(null);
const cardClasses = 'card preset-outlined-surface-300-700 size-24 grid place-items-center p-4';
const buttonClasses = 'col-span-2 btn preset-filled';
return (
<div className="grid grid-cols-2 gap-4">
{/* Source */}
<div className={cardClasses}>
<Portal disabled={disabled} target={ref.current ?? undefined}>
<SkullIcon className="size-8" />
</Portal>
</div>
{/* Target */}
<div className={cardClasses} ref={ref}>
{/* (the content will appear here) */}
</div>
{/* Trigger */}
<button className={buttonClasses} onClick={() => setDisabled(!disabled)}>
{disabled ? 'Enable' : 'Disable'}
</button>
</div>
);
}
How It Works
When enabled, the content will move from the source to the target element.
API Reference
Root
| Property | Default | Type |
|---|---|---|
disabled | false | boolean | undefinedIf true, the portal functionality is disabled and children are rendered in place. |
target | document.body | HTMLElement | undefinedThe HTML element to which the portal content will be appended. |
children | - | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> | null |