Portal & PortalTarget

The Portal and PortalTarget components are designed to make it easy to render content into a different part of the DOM. This is useful for scenarios such as rendering modals, popovers, or other UI elements that should not be restricted by parent container overflow or need to be placed in a specific area of the page for accessibility or UX considerations.

Import

import { Portal, PortalTarget } from '@frontile/overlays';

Server-Side Rendering Support

The Portal and PortalTarget components are fully compatible with server-side rendering (SSR). This ensures that your content is properly rendered and accessible, even when the application is being served from the server, making these components suitable for SEO and initial page load performance considerations.

Usage

Basic Example

The Portal component renders its content to a specified target or, by default, to the closest available target. You can use it to decouple your UI elements from their usual place in the DOM.

import { Portal, PortalTarget } from '@frontile/overlays';

<template>
  <Portal>
    <div class='p-4 border rounded shadow-md'>
      This content is rendered in a portal.
    </div>
  </Portal>
</template>

Nesting Portals

You can also nest Portal components within each other. The inner Portal elements can append to the parent portal if configured that way.

import { Portal, PortalTarget } from '@frontile/overlays';

<template>
  <div class='h-40 relative w-32'>
    <PortalTarget />
  </div>

  <Portal
    class='absolute w-content bg-default/50 left-0 p-4 border border-default rounded'
  >
    First portal (Outer)
    <Portal
      class='absolute w-content bg-primary/50 border border-primary left-12 rounded'
    >
      Second portal (Inner)
      <Portal
        class='absolute w-content bg-danger/50 border border-danger left-16 rounded'
      >
        Last portal (Inner 2)
      </Portal>
    </Portal>
  </Portal>
</template>

In this example, the inner Portal renders its content inside the destination of the outer Portal.

Using PortalTarget

The PortalTarget component is used to create named or unnamed locations where content can be rendered using the Portal component. This allows for more control over where a portal's content is displayed.

import { Portal, PortalTarget } from '@frontile/overlays';

<template>
  <div class='flex flex-col space-y-4'>
    <PortalTarget />
    <PortalTarget @for='target-1' />

    <Portal @target='target-1'>
      <div class='p-4 border rounded shadow-md'>
        Rendered to target-1
      </div>
    </Portal>
  </div>
</template>

In this example, the portal content is directed to the named target, target-1, ensuring it is rendered in the appropriate place within the DOM.

Controlling Where Content Renders

The Portal component has a target argument that allows you to specify where the content should be rendered. This can be:

  • A DOM Element directly.
  • An ID prefixed with # to target a specific element by its ID.
  • A target name to match a PortalTarget with the @for argument.

Rendering Inline

If you set the @renderInPlace argument to true, the content will be rendered inline without creating a new portal.

This content is rendered inline instead of a portal.
import { Portal, PortalTarget } from '@frontile/overlays';

<template>
  <div class='p-4 border rounded'>
    <Portal @renderInPlace={{true}}>
      This content is rendered inline instead of a portal.
    </Portal>
  </div>
</template>

Rendering Inside a Specific DOM Element

You can use a target element directly by passing its ID or a reference to the @target argument.

Target Element
import { Portal, PortalTarget } from '@frontile/overlays';

<template>
  <div id='target' class='border p-4'>
    Target Element
  </div>
  <Portal @target='#target'>
    <div class='p-4 border rounded shadow-md'>
      Rendered inside target element.
    </div>
  </Portal>
</template>

In this example, the portal content is rendered inside the element with the ID target.

Append to Parent Portal

By default, Portal components append their content to the parent portal if one exists. This behavior can be disabled by setting the @appendToParentPortal argument to false.

import { Portal, PortalTarget } from '@frontile/overlays';

<template>
  <PortalTarget />

  <Portal>
    Outer portal content
    <Portal @appendToParentPortal={{false}}>
      This content is rendered separately, not appended to the parent portal.
    </Portal>
  </Portal>
</template>

In this example, the inner Portal will not append its content to the parent portal, resulting in the content being rendered at a different level.

Accessibility and Considerations

  • Ensure that portal content, especially dynamic elements like modals and popovers, has a clear focus and can be accessed by keyboard navigation.
  • Portals are useful when elements should escape any parent overflow: hidden constraints.

The Portal and PortalTarget components provide a robust way to manage dynamic content placement within your Ember.js applications, making them a powerful tool for managing modals, popovers, tooltips, and more.

API

PortalTarget

Element: HTMLElement

Arguments

Name Type Default Description
for string - Target name

Blocks

Name Type Default Description
default * Array -

Portal

Element: HTMLDivElement

Arguments

Name Type Default Description
appendToParentPortal boolean true
renderInPlace boolean false Whether to render in place or in the specified/default destination
target enum - The target where to render the portal. There are 3 options: 1) `Element` object, 2) element id, 3) portal target name. For element id, string must be prefixed with `#`. If no value is passee in, we will render to the closest unnamed portal target, parent portal or `document.body`.

Blocks

Name Type Default Description
default * Array -
First portal (Outer)
Second portal (Inner)
Last portal (Inner 2)
Rendered to target-1
Outer portal content
This content is rendered separately, not appended to the parent portal.
Released under MIT License - Created by Josemar Luedke