DEV Community

CHOKANGYEOL
CHOKANGYEOL

Posted on

Handling React Dialog Flows with async/await

React dialogs often start simple.

You add an isOpen state, then a selected item state, then confirm/cancel callbacks, then another dialog after the first one.

Eventually, a simple flow can become scattered across multiple components.

For example, a user flow like this:

  1. Select a user
  2. Confirm the action
  3. Add the user

often becomes multiple pieces of state:

const [isUserSearchOpen, setIsUserSearchOpen] = useState(false);
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
Enter fullscreen mode Exit fullscreen mode

This works, but the actual flow is harder to read.

What if dialogs could be handled as async flows?

I wanted the code to read closer to the user flow:

const user = await openAsync(UserSearchDialog);
if (!user) return;

const confirmed = await openAsync(ConfirmDialog, {
  title: `Add ${user.name}?`,
});

if (confirmed) {
  await addUser(user.id);
}
Enter fullscreen mode Exit fullscreen mode

The dialog opens, waits for a result, and the caller continues based on that result.

This is about orchestration, not UI

This is not meant to replace Radix, MUI, Headless UI, shadcn/ui, or custom dialog components.

Those libraries solve the dialog UI problem well.

The idea here is to manage the flow around dialogs:

  • opening dialogs from anywhere under a provider
  • resolving typed result values
  • handling nested dialogs
  • distinguishing completed vs dismissed
  • supporting dismissal reasons
  • guarding close behavior with shouldClose

So the actual dialog UI can still be your own component.

I packaged the pattern

I turned this idea into a small open-source library called react-dialog-flow.

It provides a headless dialog stack, Promise-based openAsync, typed results, nested dialogs, closeTop, closeAll, dismissal reasons, shouldClose, and optional UI primitives.

GitHub: https://github.com/CHOKANGYEOL/react-dialog-flow

npm: https://www.npmjs.com/package/react-dialog-flow

Docs: https://dialog-flow.kangyeol.com/

It is still early, so I am mainly looking for feedback on the API design.

Would you use async/await for dialog flows in React, or would you keep this logic inside app-specific hooks?

Top comments (0)