# selfstore-join
Source: https://selfstore.dev/docs/widget-join
Reference for the join widget - preview an invitation, join on an explicit yes, and get named outcomes for a spent invite or a device already following another share.
Opening an invitation link. It previews first, joins on an explicit yes, and
gives **named outcomes** instead of generic errors - "this device already
follows another share" is a different sentence from "this invitation is spent",
and the user can act on each.
```html
```
An invitation, previewed before anything is joined. Unstyled apart from the accent: this is the widget with one CSS custom property set.
## Properties
| Property | Type | Default | Notes |
| --- | --- | --- | --- |
| `engine` | `JoinEngine \| null` | `null` | Your transport. Assign last. |
| `link` | `string \| null` | `null` | The invitation. Also an attribute. |
| `options` | `{ deadlineMs?: number }` | `{}` | Raise it when the engine's join opens a popup |
| `variant` | `'card' \| 'banner'` | `'card'` | Also an attribute |
| `flow` | `JoinFlow \| null` (read-only) | `null` | The underlying flow |
### `options.deadlineMs`
Set it when joining opens an account chooser. The default network guard is
generous for a request and short for a human reading a popup - a deadline that
fires while someone is picking an account looks like a failure that is not one.
### `variant`
`card` renders the full invitation: who shared, what level, the accept button.
`banner` collapses it to one line - label, join, account switch - for a page
that renders its own preview around the widget.
## Attributes
| Attribute | Values |
| --- | --- |
| `link` | the invitation URL |
| `variant` | `card` (default), `banner` |
```html
```
## Events
| Event | Detail | When |
| --- | --- | --- |
| `selfstore-joined` | `{ outcome: 'joined' }` | The device joined the share |
| `selfstore-join-refused` | `{ outcome }` | A named refusal; `outcome` is the step reached |
| `selfstore-cancelled` | none | The user backed out |
`selfstore-join-refused` is not an error event. It fires for the two outcomes
the journey expects and names - a spent invitation, or a device already
following another share - so your app can offer the right next step rather than
a retry button that will fail again.
## Parts
Shared: `title`, `sub`, `hint`, `row`, `button`, `button-primary`, `link`,
`status`, `status-ok`, `status-error`, `spinner`.
Its own: `banner` - the row in the `banner` variant (also carries `row`).
## Labels
Every string is a key with a default, and the widget ships English and French. The 14 keys it owns are listed on [every label key](https://selfstore.dev/docs/widget-labels#selfstore-join); how the resolution works is on [wording](https://selfstore.dev/docs/widgets-localization).
Map of this site for a model: https://selfstore.dev/llms.txt
Every page in one file: https://selfstore.dev/llms-full.txt