Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Invite Members Dialog Block for Radix UI

A household calendar's Members page with its invite dialog shown open: one email field that knows the household's domain, so an outside address joins as a guest, the people who already have access, and a Send invite button that adds a pending row.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let people bring the rest of the household in, with this invite dialog for React with Radix UI. It opens from the Members page of Nestcal, a shared family calendar: the page's header holds an outline Invite Button, and the page itself is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog holds one email Input in a Field that reads the address as you type: one on the household's own domain joins as a member who can edit events, anyone else as a guest who only sees shared ones, and the line under the field says which before you send. Below it the people who already have access are listed with an initials Avatar and a role Badge, the pending invite marked with a clock and its age. Send invite, the only Shine Button, checks the address, adds a pending row at the top and closes. For preview the dialog is drawn open in place when the page loads; the Invite button opens the real Dialog, with the same field and the same list. For family and team calendars, shared workspaces and any app with members.

FAQ

Why is the dialog already open, and what do I keep?

So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Invite button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same InviteFields component and share the block's state, so an address typed in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

How does it decide between a member and a guest?

From the address. DOMAIN is the household's own domain: an address on it joins as a member, any other as a guest, and the line under the field says which as you type, so there are no surprises after you send. The role is a promise your server has to keep: a guest sees shared events only because your API says so, not because of this dialog. To let the inviter choose instead, put a Select or a Segment Group under the field and pass its value with the address.

What does Send invite do, and how do I wire it up?

Here it adds the person to the list in local state as pending, with "invited in this demo", closes the dialog and says in a polite status message that nothing was sent. No request goes out and no email is sent. In your app post the address to your server in invite, let the server send the email with its join link, and add the row from the response; if the server refuses the address, show its reason in the field's error line and keep the dialog open.

How is the address checked?

When you leave the field and when you send: an empty field asks for an address, a malformed one shows the expected shape, and an address that already has access, or already has an invite waiting, says so. The error sits under the field with an icon, in the slot the hint uses, so the list never moves; once it shows, it follows your typing and clears as soon as the address is fixed. Sending with a problem keeps the dialog open and puts focus back in the field.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts in the email field and stays inside until it closes, and Escape, Cancel and the close button all return focus to Invite. The trigger is the Invite button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on Invite. One polite status message reports each invite.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet, its actions pinned at the bottom with Send invite on top, and the field and the list scroll above them. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.