# Portal `portal` is a low-level **render-target primitive**: it renders its children into a *different* part of the DOM — by default the end of `document.body` — instead of where the tag sits in the page. That lets overlays, popovers, and tooltips escape a parent's `overflow: hidden` or its stacking context (`z-index`) so they're never clipped. It has no visual styling of its own, so on a static docs page the portalled content simply appears at the bottom of the page rather than inline — that's the primitive working as intended, not a bug. You'll mostly meet it indirectly, inside Modal, Drawer, and Tooltip. Use it as `{% portal %}` in Markdown, or call it from Python logic (loops, snippets) via `component('aardvark', 'portal', …)`. ## Demonstrations The block body is the content that gets portalled out. By default it mounts at the end of `document.body`, so on this page it renders below the normal content flow rather than at this exact spot. **Preview** (the portalled text appears at the bottom of the page, not here): This paragraph was portalled into document.body by the Portal docs page.
Source: Markdown ```aardvark {% portal %} This paragraph is rendered into document.body, not here inline. {% endPortal %} ``` Source: Python ```python component('aardvark', 'portal', children='This paragraph is rendered into document.body, not here inline.') ``` ### Targeting a specific node Pass `target` (a CSS selector or element id) to mount into a specific element instead of `document.body`. This is the same mechanism Mantine's overlay components use under the hood; reach for the tag directly only when you're building a custom overlay yourself. **Preview** — the body mounts into `#my-overlay-root` if that element exists, otherwise into `document.body`:
Source: Markdown ```aardvark {% portal target='#my-overlay-root' %} …content rendered into #my-overlay-root… {% endPortal %} ``` Source: Python ```python component('aardvark', 'portal', target='#my-overlay-root', children='…content rendered into #my-overlay-root…') ``` ### Reusing a single target node Set `reuseTargetNode=true` so many portals share one generated target node instead of each creating its own — handy when you portal a lot of small fragments and don't want a node per portal.
Source: Markdown ```aardvark {% portal reuseTargetNode=true %} …content sharing a single reused target node… {% endPortal %} ``` Source: Python ```python component('aardvark', 'portal', reuseTargetNode=True, children='…content sharing a single reused target node…') ``` ## With other components Portal is most useful wrapping content that must escape a clipped container. Here a [Paper](/components/layout/paper/) surface is portalled to `document.body` so it can't be cut off by an ancestor's `overflow: hidden`. **Preview** (the Paper renders at the bottom of the page): This Paper was portalled to document.body so it escapes any clipping ancestor.
Source: Markdown ```aardvark {% portal %} {% paper withBorder=true p='md' radius='md' %}This Paper was portalled to document.body so it escapes any clipping ancestor.{% endPaper %} {% endPortal %} ``` Source: Python ```python component('aardvark', 'portal', children=component('aardvark', 'paper', withBorder=True, p='md', radius='md', children='This Paper was portalled to document.body.')) ``` ## Attributes Omit any attribute to take its default. Bare flags (e.g. `reuseTargetNode`) become `=True`. | Attribute | Valid values | Description | | --- | --- | --- | | `target` | A CSS selector or element id (string) | The element to render *into*, instead of `document.body`. | | `reuseTargetNode` | `true` / `false` (default `false`) | Reuse a single shared target node across portals, rather than creating one per portal. | | `attr={…}` | An object of HTML attributes | Forwards raw HTML attributes onto the rendered element. | ## CSS Selectors Each `portal` carries `data-aardvark-island="Portal"` on its wrapper; it renders its content into `document.body` with no wrapper element, so target the island wrapper. ```css [data-aardvark-island="Portal"] { /* style every portal on the page */ } ``` ## Injecting Attributes `attr={…}` forwards raw HTML attributes (including event handlers) straight onto the rendered element. This paragraph is portalled into document.body.
Source: Markdown ```aardvark {% portal attr={'onclick': ''' const value = this.innerText; console.log('attr demo value:', value); alert(value); '''} %} This paragraph is portalled into document.body. {% endPortal %} ``` Source: Python ```python component('aardvark', 'portal', children='This paragraph is portalled into document.body.', attr={'onclick': ''' const value = this.innerText; console.log('attr demo value:', value); alert(value); '''}) ```