Entry Points

Internal use only. These components are available only within Atlassian.

Why use entry points?

Entry points load a UI surface and its data when a user needs it. This can keep code and data for infrequent actions out of the initial experience while retaining a familiar trigger and component interaction.

Use an entry point when the surface is genuinely asynchronous and the loading and failure states are part of the experience. Start with the original component guidance, then use the trigger that matches the surface you need.

You can learn more about the architecture in the EntryPoints documentation, (opens new window).

How entry points are constructed

This complete modal example follows the same pattern as the modal trigger basic example. It omits the Relay environment provider because your application already supplies it.

1. Define the entry point types

edit-issue-modal.entrypoint.types.tsx describes the component the entry point will load. It names one preloaded query and gives the loaded UI access to the modal's onClose runtime prop.

import type { EntryPointComponent } from 'react-relay';

import type { ModalComponentProps } from '@atlassian/entry-points/modal-context-provider';

import type { EditIssueModalQuery } from './__generated__/EditIssueModalQuery.graphql';

export type EditIssueModalEntryPointProps = ModalComponentProps;

export type EditIssueModalEntryPointComponent = EntryPointComponent<
    { mainQuery: EditIssueModalQuery },
    Record<string, never>,
    EditIssueModalEntryPointProps,
    Record<string, never>
>;

2. Define the asynchronously loaded entry point and its data

edit-issue-modal.entrypoint.tsx loads the modal UI and its one mainQuery when the trigger preloads or opens it. The query receives the issueId supplied by the trigger.

import type { EntryPoint } from 'react-relay';

import { JSResourceForInteraction } from '@atlassian/react-async';
import { createEntryPoint } from '@atlassian/react-entrypoint';

import EditIssueModalQueryNode from './__generated__/EditIssueModalQuery.graphql';
import type { EditIssueModalEntryPointComponent } from './edit-issue-modal.entrypoint.types';

export const editIssueModalEntryPoint: EntryPoint<
    EditIssueModalEntryPointComponent,
    { issueId: string }
> = createEntryPoint({
    root: JSResourceForInteraction<EditIssueModalEntryPointComponent>(
        /* webpackChunkName: "async-edit-issue-modal" */ './edit-issue-modal.tsx',
    ),
    getPreloadProps: ({ issueId }: { issueId: string }) => ({
        queries: {
            mainQuery: {
                parameters: EditIssueModalQueryNode,
                variables: { issueId },
            },
        },
    }),
});

3. Render the loaded modal UI and consume the data

edit-issue-modal.tsx reads the preloaded query and renders the modal's content. It receives onClose through the entry point's runtime props, so every dismissal control closes the modal.

import React from 'react';

import { type EntryPointProps, graphql, usePreloadedQuery } from 'react-relay';

import Button from '@atlaskit/button/default/button';
import ModalBody from '@atlaskit/modal-dialog/modal-body';
import ModalFooter from '@atlaskit/modal-dialog/modal-footer';
import ModalHeader from '@atlaskit/modal-dialog/modal-header';
import ModalTitle from '@atlaskit/modal-dialog/modal-title';

import type { EditIssueModalQuery } from './__generated__/EditIssueModalQuery.graphql';
import type { EditIssueModalEntryPointProps } from './edit-issue-modal.entrypoint.types';

type Props = EntryPointProps<
    { mainQuery: EditIssueModalQuery },
    Record<string, never>,
    EditIssueModalEntryPointProps,
    Record<string, never>
>;

export default function EditIssueModal({
    queries: { mainQuery },
    props: { onClose },
}: Props): React.JSX.Element {
    const data = usePreloadedQuery<EditIssueModalQuery>(
        graphql`
            query EditIssueModalQuery($issueId: ID!) {
                jira @required(action: THROW) {
                    issueById(id: $issueId) @required(action: THROW) {
                        key @required(action: THROW)
                    }
                }
            }
        `,
        mainQuery,
    );

    return (
        <>
            <ModalHeader hasCloseButton>
                <ModalTitle>Edit issue</ModalTitle>
            </ModalHeader>
            <ModalBody>Editing {data.jira.issueById.key}</ModalBody>
            <ModalFooter>
                <Button appearance="subtle" onClick={onClose}>
                    Cancel
                </Button>
                <Button appearance="primary" onClick={onClose}>
                    Save
                </Button>
            </ModalFooter>
        </>
    );
}

4. Attach the entry point to a trigger

edit-issue-button.tsx connects the entry point to the base modal experience and supplies its issueId. ModalContextProvider must appear once at the application root; it is shown here to make this standalone example complete. Pass the supplied ref to the interactive trigger so it can preload and open the modal.

import React, { type Ref } from 'react';

import Button from '@atlaskit/button/default/button';
import { ModalContextProvider } from '@atlassian/entry-points/modal-context-provider';
import { ModalTrigger } from '@atlassian/entry-points/modal-trigger';

import { editIssueModalEntryPoint } from './edit-issue-modal.entrypoint';

export default function EditIssueButton(): React.JSX.Element {
    return (
        <ModalContextProvider>
            <ModalTrigger
                entryPoint={editIssueModalEntryPoint}
                entryPointParams={{ issueId: '10001' }}
                title="Edit issue"
                loadingText="Loading issue"
            >
                {({ ref }) => (
                    <Button ref={ref as Ref<HTMLButtonElement>} aria-haspopup="dialog">
                        Edit issue
                    </Button>
                )}
            </ModalTrigger>
        </ModalContextProvider>
    );
}

The definition and related type files are application boilerplate. This documentation focuses on the trigger and the user-facing loading and error experiences. See the internal EntryPoints API documentation, (opens new window) for data dependencies, nested entry points, and application-level setup.

Components

Shared usage guidelines

  • Read the original component documentation before adding an asynchronous trigger. Its interaction, content, and accessibility requirements still apply after content is loaded.
  • Keep the trigger available while the entry point loads. Preloading is not a user action: it must not commit a change, select a menu item, or open a surface on its own. Users must be able to dismiss the loading state with the component's normal controls.
  • Use clear, internationalized text for every trigger label, surface name, loading state, error state, and loaded content. Internationalization is required for users to understand this text in their language. When you replace a default fallback, translate the text inside your custom fallback too.
  • Use fallback for loading content and errorFallback for a recoverable failure presentation when the trigger supports them. Keep those states distinct from the component's available actions. Only offer retry when it starts a fresh request and can recover.
  • Each trigger's Usage page documents its additional requirements. Modal triggers also require one Modal context provider at the application root.

Other entry point components

The entry point ecosystem includes components and utilities maintained by other teams. They are not covered by this Design System documentation. Check their source and owner-maintained documentation before using them.

Was this page helpful?
We use this feedback to improve our documentation.
© 2026 AtlassianTrademark, (opens new window)Privacy, (opens new window)License