Imperative Surface Imperative Surface

SSR Considerations

Imperative Surface uses window, document, document.body, and createRoot() when a surface is presented.

The presentation APIs are therefore browser-only.

Do not present during render

Avoid:

// Incorrect
const result = Surface.Dialog.present(...);

during server rendering or component render.

Instead, call it from a client event handler, effect, or another browser-only interaction:

'use client';

async function openDialog() {
  const result = await Surface.Dialog.present({
    body: MyDialog,
  });
}

What happens on the server?

The implementation checks:

if (typeof window === 'undefined') {
  return reject('Window not available');
}

This means a server-side call rejects rather than attempting to access the DOM.

Keep the page and data loading on the server when useful, then isolate interactive surface triggers into client components.

// Server component
export default async function Page() {
  const data = await getData();

  return <InteractiveActions data={data} />;
}
// Client component
'use client';

export function InteractiveActions({ data }) {
  // Surface.Dialog.present(...)
}

On this page