@teamvortexsoftware/vortex-react
v1.1.0
Published
Vortex Components
Readme
Vortex React Component
High-performance React component for Vortex invitations with intelligent provider integration.
Quick Start
npm install @teamvortexsoftware/vortex-reactBasic Usage (Standalone)
import { VortexInvite } from '@teamvortexsoftware/vortex-react';
function MyComponent() {
return (
<VortexInvite
component="00000000-0000-0000-0000-000000000000"
presentation="modal"
open={true}
user={{ id: 'user-id', email: '[email protected]' }}
scope="org-id"
vars={{
workspace_name: '',
inviter_name: '',
workspace_member_count: '',
group_name: '',
group_member_count: '',
}}
/>
);
}Fetching a Token
You need to fetch a JWT token from your backend before rendering the widget:
import { VortexInvite } from '@teamvortexsoftware/vortex-react';
import { useState, useEffect } from 'react';
function MyComponent() {
const [token, setToken] = useState<string>('');
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch('/api/vortex/jwt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
.then((res) => res.json())
.then((data) => {
setToken(data.token);
setIsLoading(false);
})
.catch((error) => {
console.error('Failed to fetch token:', error);
setIsLoading(false);
});
}, []);
return (
<VortexInvite
component="00000000-0000-0000-0000-000000000000"
presentation="modal"
open={true}
user={{ id: 'user-id', email: '[email protected]' }}
scope="org-id"
vars={{
workspace_name: '',
inviter_name: '',
workspace_member_count: '',
group_name: '',
group_member_count: '',
}}
token={token}
isLoading={isLoading}
/>
);
}Signature Authentication (Alternative to JWT)
Instead of fetching a JWT token, you can use HMAC signature authentication — a simpler alternative for many use cases:
import { VortexInvite } from '@teamvortexsoftware/vortex-react';
function MyComponent() {
return (
<VortexInvite
component="00000000-0000-0000-0000-000000000000"
user={{ id: 'user-123', email: '[email protected]' }}
signature={signatureFromBackend}
scope="team-123"
presentation="modal"
open={true}
/>
);
}Generate the signature on your backend using any Vortex SDK's sign() method. The signature format is kid:hexdigest (e.g., "key-abc123:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08").
See your backend SDK's README for details on generating signatures.
Complete Props Reference
Core Props
| Prop | Type | Required | Description | Docs |
| -------------- | ------------------------------------------------ | -------- | ----------------------------------------------------------- | --------------------------------------------------------------------------- |
| component | string | Yes | Component identifier (UUID) | docs |
| token | string | No | Secure JWT token from your backend | docs |
| scope | string | Yes | Scope identifier (e.g., team ID) | docs |
| scopeType | string | No | Scope type (e.g., "team") | docs |
| presentation | 'modal' \| 'embed' | No | UI mode. Defaults to "embed". | |
| open | boolean | No | Controls modal visibility when presentation="modal". | |
| user | string \| UnsignedData \| object | No | User identifier or user data object | |
| signature | string | No | HMAC signature for authentication (format: kid:hexdigest) | |
| isLoading | boolean | No | Loading state indicator | docs |
| loading | VortexLoading & { render?: () => JSX.Element } | No | Loading state configuration with optional custom render | |
| vars | Record<string, string> | No | Template variables | docs |
| locale | string | No | Locale code for internationalization | |
| env | 'dev' \| 'prod' | No | Target environment for insecure (raw-data) tokens | |
| metadata | Record<string, any> | No | Custom metadata object | |
Callbacks
| Prop | Type | Description |
| ----------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| onEvent | (event: any) => void | Event handler for component events |
| onSubmit | (data: { formData: any; result: WidgetConfigurationForWidgetDto \| undefined }) => void | Form submission handler |
| onSubmitSuccess | (data: VortexInviteSubmitSuccess) => void | Called when the invitation is created successfully. Suppresses the built-in success UI. |
| onSubmitError | (data: VortexInviteSubmitError) => void | Called when submission fails. Suppresses the built-in error UI. data.errorCode is a VortexInviteErrorCode. |
| onInvite | (data: OnInviteData) => void | Invitation completion handler |
| onError | (error: any) => void | Error handler |
Validation, Autocomplete & Form Customization
| Prop | Type | Description | Docs |
| ------------------------- | ----------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------- |
| emailValidationFunction | EmailGroupMembershipCheckFunction | Custom email validation | docs |
| autocompleteCallback | AutocompleteCallback | Autocomplete handler | |
| dynamicValuesCallback | DynamicValuesCallback | Dynamic values handler | |
| formElementAttributes | FormElementAttributesMap | Form element attributes | |
Data & Contacts
| Prop | Type | Description | Docs |
| ----------------------- | -------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------- |
| analyticsSegmentation | Record<string, any> | Analytics tracking data | |
| userEmailsInGroup | string[] | Pre-populated email list | docs |
| pymk | Array<{ internalId, name, mutualContactCount?, avatarUrl? }> | People You May Know suggestions | docs |
| groups | Array<{ type, id?, groupId?, name }> | Group list | |
| group | { type, id?, groupId?, name } | Single group | |
| googleAppClientId | string | Google OAuth client ID for Contacts import | docs |
| unfurlConfig | { title?, description?, image?, siteName?, type? } | Link preview unfurl configuration | |
Deprecated Props
These props still work for backward compatibility but will be removed in a future major version.
| Prop | Type | Replacement |
| ------------------- | ------------------------ | ------------------------------------------------------------------------------------- |
| componentId | string | Use component instead |
| widgetId | string | Use component instead |
| jwt | string | Use token instead |
| templateVariables | Record<string, string> | Use vars instead |
| googleAppApiKey | string | No longer required. Google Contacts import now uses only OAuth (googleAppClientId). |
Backward Compatibility
100% backward compatible - existing implementations work unchanged:
// This still works exactly as before
<VortexInvite
componentId="00000000-0000-0000-0000-000000000000"
jwt={jwt}
isLoading={loading}
scope="team-123"
/>Advanced Examples
Modal Mode
Use presentation="modal" to render the widget as an overlay. Control visibility with the open prop.
import { VortexInvite } from '@teamvortexsoftware/vortex-react';
import { useState } from 'react';
function MyComponent() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(true)}>Invite People</button>
<VortexInvite
component="00000000-0000-0000-0000-000000000000"
presentation="modal"
open={isOpen}
token={token}
scope="team-123"
onInvite={(data) => {
console.log('Invitation sent:', data);
setIsOpen(false);
}}
/>
</>
);
}With Custom Submit Callbacks
Use onSubmitSuccess and onSubmitError to handle submission outcomes in your own UI. When either callback is provided the built-in success/error banner inside the widget is suppressed.
import { VortexInvite, VortexInviteErrorCode } from '@teamvortexsoftware/vortex-react';
import type {
VortexInviteSubmitSuccess,
VortexInviteSubmitError,
} from '@teamvortexsoftware/vortex-react';
<VortexInvite
component="00000000-0000-0000-0000-000000000000"
token={token}
scope="team-123"
presentation="modal"
open={true}
onSubmitSuccess={(data: VortexInviteSubmitSuccess) => {
console.log('Invitation created:', data.result);
showToast('Invitation sent!');
}}
onSubmitError={(data: VortexInviteSubmitError) => {
if (data.errorCode === VortexInviteErrorCode.alreadyInvited) {
showToast('That person has already been invited.');
} else if (data.errorCode === VortexInviteErrorCode.alreadyAMember) {
showToast('That person is already a member.');
} else if (data.errorCode === VortexInviteErrorCode.emailDomainRestriction) {
showToast('Invitations are restricted to specific domains.');
} else {
showToast(`Error: ${data.message}`);
}
}}
/>;With Custom Event Handlers
<VortexInvite
component="advanced-widget"
token={token}
scope="team-123"
presentation="modal"
open={true}
onInvite={(data) => {
console.log('Invitation sent:', data);
trackAnalyticsEvent('invitation_sent', data);
}}
onError={(error) => {
console.error('Invitation error:', error);
showErrorToast(error.message);
}}
/>With Custom Validation
<VortexInvite
component="validated-widget"
token={token}
scope="team-123"
presentation="modal"
open={true}
emailValidationFunction={async (email) => {
const isValid = await validateEmailInSystem(email);
return {
valid: isValid,
message: isValid ? 'Valid' : 'Email not found in system',
};
}}
/>With People You May Know (PYMK)
<VortexInvite
component="pymk-widget"
token={token}
scope="team-123"
presentation="modal"
open={true}
pymk={[
{
internalId: '123',
name: 'John Doe',
mutualContactCount: 5,
avatarUrl: 'https://example.com/avatar1.jpg',
},
{
internalId: '456',
name: 'Jane Smith',
mutualContactCount: 3,
avatarUrl: 'https://example.com/avatar2.jpg',
},
{
internalId: '789',
name: 'Bob Johnson',
mutualContactCount: 1,
},
]}
/>The pymk prop allows you to surface suggested connections to users. The widget will automatically sort them by mutualContactCount (descending). The avatarUrl is optional.
With Template Variables
<VortexInvite
component="templated-widget"
token={token}
scope="team-123"
presentation="modal"
open={true}
vars={{
companyName: 'Acme Corp',
userName: 'John Doe',
customMessage: 'Join our team!',
}}
/>Mixed Provider and Explicit Props
<VortexProvider config={{ apiBaseUrl: '/api/vortex' }}>
<VortexInvite
component="hybrid-widget"
// jwt, isLoading automatically from provider
scope="workspace-456"
analyticsSegmentation={customAnalytics}
onInvite={handleInvite}
/>
</VortexProvider>TypeScript Support
Full TypeScript support with exported interfaces:
import type { VortexInviteProps } from '@teamvortexsoftware/vortex-react';
const MyComponent: React.FC<{ config: VortexInviteProps }> = ({ config }) => {
return <VortexInvite {...config} />;
};Error Handling
The component gracefully handles all scenarios:
- No Provider: Uses explicit props only
- Provider Error: Falls back to explicit props
- Missing Props: Safe defaults applied
- Mixed Usage: Explicit props override provider
Best Practices
1. Dead Simple with Provider
<VortexProvider config={{ apiBaseUrl: '/api/vortex' }}>
{/* All components automatically get jwt and isLoading */}
<VortexInviteWithProvider
component="widget-1"
scope="team-1"
presentation="modal"
open={true}
onInvite={handleInvite1}
/>
<VortexInviteWithProvider
component="widget-2"
scope="team-2"
presentation="modal"
open={true}
onInvite={handleInvite2}
/>
<VortexInviteWithProvider
component="widget-3"
scope="workspace-1"
presentation="modal"
open={true}
onInvite={handleInvite3}
/>
</VortexProvider>2. Override Provider Data When Needed
<VortexInviteWithProvider
component="special-widget"
scope="special-team"
presentation="modal"
open={true}
token={customToken} // Override provider token for this instance
// isLoading still comes from provider automatically
/>3. Standalone for Simple Cases
// No provider needed for simple, one-off usage
<VortexInvite
component="simple-widget"
token={staticToken}
isLoading={false}
scope="team-123"
presentation="modal"
open={true}
onInvite={handleInvite}
/>What's New
- Two Component Options - Choose
VortexInvite(traditional) orVortexInviteWithProvider(dead simple) - Friendly Prop Names -
component,token,varsreplace verbose legacy names - Modal Mode - Use
presentation="modal"withopento render as an overlay - 100% Backward Compatible - Existing
VortexInviteusage works unchanged - Dead Simple Experience -
VortexInviteWithProvidereliminates jwt and isLoading props - Explicit Override - Your props always override provider values
- Zero Configuration - Just wrap with VortexProvider and use
VortexInviteWithProvider - Safe Imports - No crashes when provider package isn't installed
Advanced: Multiple Widgets
If you have multiple Vortex widgets across your app and want to share JWT state, consider using @teamvortexsoftware/vortex-react-provider. However, for most apps with a single widget, the standalone pattern shown above is simpler and recommended.
See the provider package for details (note: in maintenance mode).
Related Packages
@teamvortexsoftware/vortex-nextjs-15-sdk- Next.js API route handlers@teamvortexsoftware/vortex-react-provider- Context provider (maintenance mode - use only for multiple widgets)
Need help? Check out the demo implementation for complete examples.
