React wrapper for VGS Collect.js fields
Explore the docs »
Report Bug
·
Request Feature
- Overview
- Installation
- How to use
- Tokenization API
- Integration with Card Management Platform
- Legacy
VGSCollectFormAPI - Documentation
- Examples
- Contact
- License
VGS Collect.js is a JavaScript library that allows you to securely collect data via any form. Instantly create custom forms that adhere to PCI, HIPAA, GDPR, or CCPA security requirements. VGS intercepts sensitive data before it hits your servers and replaces it with aliased versions while securing the original data in our vault. The form fields behave like traditional forms while preventing access to unsecured data by injecting secure iframe components.
This package provides a convenient way to use VGS secure frames in the React environment by exposing field components, hooks, and a session wrapper that abstracts over VGSCollect.session() / VGSCollect.create().
It exports two top-level wrappers:
VGSCollectSession— recommended for new integrations. Built on top ofVGSCollect.session()and supports proxy, vault, tokenization, and Card Management Platform (CMP) flows through a single declarativesubmitprop.VGSCollectForm— the original wrapper aroundVGSCollect.create(). Still supported for backward compatibility — see LegacyVGSCollectFormAPI.
Install the package using npm:
npm install @vgs/collect-js-reactTo stay PCI Compliant it is mandatory to load collect.js from the js.verygoodvault.com domain. There are two common options:
- Download the file directly from the CDN.
- Use the
@vgs/collect-jsloader. Example:examples/demo/src/features/Basic.tsx.
import { useEffect, useState } from 'react';
import { loadVGSCollect } from '@vgs/collect-js';
const [isScriptLoaded, setScriptLoaded] = useState(false);
useEffect(() => {
loadVGSCollect({
vaultId: '<vault_id>',
environment: 'sandbox',
version: '2.25.1'
}).then(() => setScriptLoaded(true));
}, []);Render the secure form only after loadVGSCollect has resolved.
import {
VGSCollectSession,
VGSCollectFormState,
VGSCollectHttpStatusCode
} from '@vgs/collect-js-react';
const App = () => {
const stateCallback = (state: VGSCollectFormState) => {};
const onSubmitCallback = (status: VGSCollectHttpStatusCode, resp: any) => {};
const onErrorCallback = (errors: any) => {};
return (
<VGSCollectSession
vaultId='<vault_id>'
environment='<environment>'
submit={{ api: 'proxy', action: '/post' }}
stateCallback={stateCallback}
onSubmitCallback={onSubmitCallback}
onErrorCallback={onErrorCallback}
>
{/* Add secure fields here */}
</VGSCollectSession>
);
};
export default App;| Property | Description | Documentation |
|---|---|---|
vaultId |
A string value beginning with the prefix tnt. |
Parameters.vaultId |
environment (alias: env) |
Vault environment: sandbox | live | live-eu-1 | live-ap-1. |
Parameters.environment |
submit? |
Submit configuration object — selects the flow (proxy, vault, tokenization, cmp). Defaults to a proxy submit to /. See Submit configuration. |
.submit() |
formId? |
Collect Session form id (required for CMP and configured proxy sessions). | VGSCollect.session() |
configuration? |
Inline session configuration (fallback used when formId configuration is not available, e.g. cardAttributes). |
VGSCollect.session() |
authHandler? |
String token or () => string | Promise<string | { token }> resolver used by CMP / authenticated sessions. |
VGSCollect.session() |
stateCallback? |
Returns the form state on every field update. | Parameters.stateCallback |
onUpdateCallback? |
Alias for stateCallback (kept for backward compatibility). |
— |
onSubmitCallback? |
Returns status and response data after a successful submit. | Parameters.responseCallback |
onErrorCallback? |
Receives client-side validation errors, async header rejections, or CMP/proxy errors. | — |
onCustomSubmit? |
Replace the default submit handler with your own — see Custom submit handling. | — |
onGetCardAttributesSuccess? |
CMP only. Fires when card attributes (BIN lookup) succeed. | CMP Card Attributes |
onGetCardAttributesError? |
CMP only. Fires when card attributes lookup fails. | — |
cname? |
Custom CNAME the request will be submitted to. | .useCNAME() |
routeId? |
Inbound Route ID. Can also be passed via submit.routeId for proxy/tokenization flows. |
.setRouteId() |
VGSCollectSession accepts a discriminated submit object that selects the API used when the form is submitted:
type ICollectSessionSubmit =
| { api: 'proxy'; action: string; routeId?: string; submitParameters?: Partial<VGSCollectSubmitOptions> }
| { api: 'vault'; submitParameters?: Record<string, any> }
| { api: 'tokenization'; routeId?: string }
| { api: 'cmp'; operation: 'createCard'; submitParameters?: Partial<VGSCollectSubmitOptions> }
| { api: 'cmp'; operation: 'updateCard'; params: { cardId: string; [key: string]: any } };Posts the collected values to a vault route. submitParameters mirrors the options argument of form.submit() and supports both static values and async functions for data and headers:
import { ICollectFormPayloadStructure } from '@vgs/collect-js-react';
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
submit={{
api: 'proxy',
action: '/post',
submitParameters: {
// Build a custom request body using aliased field tokens and any extra state from your app.
data: (fields: ICollectFormPayloadStructure) => ({
customData: 'some-non-sensitive-value',
textField: fields.textField,
cardNumber: fields['card-number']
}),
// Headers can be a plain object or an async function. Rejections are surfaced via onErrorCallback.
headers: async () => ({
'X-Custom-1': '1',
'X-Custom-2': '2'
})
}
}}
stateCallback={onStateCallback}
onSubmitCallback={onSubmitCallback}
onErrorCallback={onErrorCallback}
>
{/* fields */}
</VGSCollectSession>Creates aliases against the vault without performing an HTTP proxy request. submitParameters is passed through to form.createAliases().
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
submit={{ api: 'vault', submitParameters: { /* createAliases options */ } }}
>
{/* fields */}
</VGSCollectSession>See Tokenization API for the full example.
See Integration with Card Management Platform for createCard and updateCard flows.
Field components are exposed on both VGSCollectSession and VGSCollectForm.
| Collect.js input type | Component | Default props |
|---|---|---|
text |
<VGSCollectSession.TextField /> |
{ type: 'text', placeholder: 'Cardholder Name' } |
text |
<VGSCollectSession.CardholderField /> |
{ type: 'text', name: 'cardholder', placeholder: 'Cardholder' } |
card-number |
<VGSCollectSession.CardNumberField /> |
{ type: 'card-number', name: 'pan', validations: ['required', 'validCardNumber'], placeholder: 'Credit Card Number' } |
card-expiration-date |
<VGSCollectSession.CardExpirationDateField /> |
{ type: 'card-expiration-date', name: 'exp-date', validations: ['required', 'validCardExpirationDate'], yearLength: 2, placeholder: 'MM/YY' } |
card-security-code |
<VGSCollectSession.CardSecurityCodeField /> |
{ type: 'card-security-code', name: 'cvc', validations: ['required', 'validCardSecurityCode'], placeholder: 'CVC/CVV' } |
password |
<VGSCollectSession.PasswordField /> |
{ type: 'password', placeholder: 'Enter Password' } |
ssn |
<VGSCollectSession.SSNField /> |
{ type: 'ssn', placeholder: 'SSN' } |
zip-code |
<VGSCollectSession.ZipCodeField /> |
{ type: 'zip-code', placeholder: 'Zip Code' } |
number |
<VGSCollectSession.NumberField /> |
{ type: 'number', placeholder: 'Number' } |
textarea |
<VGSCollectSession.TextareaField /> |
{ type: 'textarea', placeholder: 'Comment' } |
file |
<VGSCollectSession.FileField /> |
{ type: 'file', placeholder: '' } |
date |
<VGSCollectSession.DateField /> |
{ type: 'date', placeholder: '' } |
The complete list of supported props is documented here: https://www.verygoodsecurity.com/docs/api/collect/#api-formfield. Any property in that reference can be passed to a field component using the same name (validations, css, classes, style, placeholder, autoFocus, disabled, readonly, inputMode, defaultValue, serializers, tokenization, etc.).
Example (full set of field types):
import { VGSCollectSession } from '@vgs/collect-js-react';
const {
TextField,
CardholderField,
CardNumberField,
CardExpirationDateField,
CardSecurityCodeField,
PasswordField,
TextareaField,
NumberField,
SSNField,
ZipCodeField,
FileField,
DateField
} = VGSCollectSession;
const styles = {
padding: '.5rem 1rem',
boxSizing: 'border-box',
'&::placeholder': { color: '#686868' }
};
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
submit={{ api: 'proxy', action: '/post' }}
>
<TextField name='text' validations={['required']} css={styles} />
<CardholderField name='cardholder' css={styles} defaultValue='John Doe' />
<CardNumberField name='card-number' validations={['required', 'validCardNumber']} showCardIcon={{ right: '1rem' }} css={styles} />
<CardExpirationDateField name='exp-date' validations={['required', 'validCardExpirationDate']} yearLength={2} css={styles} />
<CardSecurityCodeField name='card-security-code' validations={['required', 'validCardSecurityCode']} showCardIcon={{ right: '1rem' }} css={styles} />
<SSNField name='ssn' validations={['required', 'validSSN']} css={styles} />
<ZipCodeField name='zip-code' validations={['required']} css={styles} />
<PasswordField name='password' validations={['required']} css={styles} />
<NumberField name='number' validations={['required']} css={styles} />
<TextareaField name='textarea' validations={['required']} css={styles} />
<DateField name='date' validations={['required']} css={styles} />
<FileField name='file' validations={['required']} serializers={[{ name: 'toBase64' }]} css={styles} />
<button type='submit'>Submit</button>
</VGSCollectSession>VGS Collect.js exposes input lifecycle events on every field: onFocus, onBlur, onUpdate, onDelete, onKeyUp, onKeyDown, onKeyPress.
import {
VGSCollectFocusEventData,
VGSCollectKeyboardEventData,
VGSCollectStateParams
} from '@vgs/collect-js-react';
<TextField
name='text'
validations={['required']}
onFocus={(info: VGSCollectFocusEventData) => {}}
onBlur={(info: VGSCollectFocusEventData) => {}}
onUpdate={(info: VGSCollectStateParams) => {}}
onKeyUp={(info: VGSCollectKeyboardEventData) => {}}
onKeyDown={(info: VGSCollectKeyboardEventData) => {}}
onKeyPress={(info: VGSCollectKeyboardEventData) => {}}
/>Wrap your app in VGSCollectProvider to expose the form state, submit response, and form instance through hooks.
import {
VGSCollectProvider,
VGSCollectSession,
useVGSCollectState,
useVGSCollectResponse,
useVGSCollectFormInstance
} from '@vgs/collect-js-react';
const { TextField } = VGSCollectSession;
const App = () => (
<VGSCollectProvider>
<SecureForm />
</VGSCollectProvider>
);
const SecureForm = () => {
const [state] = useVGSCollectState();
const [response] = useVGSCollectResponse();
const [form] = useVGSCollectFormInstance();
useEffect(() => {
if (state) {
// observe per-field state
}
}, [state]);
return (
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
submit={{ api: 'proxy', action: '/post' }}
>
<TextField name='cardholder-name' validations={['required']} placeholder='Cardholder name' />
<button type='submit'>Submit</button>
</VGSCollectSession>
);
};| Hook | Returns |
|---|---|
useVGSCollectState() |
[VGSCollectFormState] — current per-field state, or null when unmounted. |
useVGSCollectResponse() |
[{ status, data }] — response from the last proxy submit. |
useVGSCollectFormInstance() |
[IVGSCollectForm] — the underlying Collect.js form instance. |
Pass onCustomSubmit to replace the default submit flow. Combine it with useVGSCollectFormInstance to drive form.submit() yourself — useful for guarding submits, retries, or chaining requests.
import {
VGSCollectSession,
useVGSCollectFormInstance,
IVGSCollectForm
} from '@vgs/collect-js-react';
const { TextField } = VGSCollectSession;
const SubmitHandling = () => {
const [form] = useVGSCollectFormInstance() as [IVGSCollectForm | null];
const [isSubmitting, setSubmitting] = useState(false);
const customHandling = (event: React.SyntheticEvent) => {
event.preventDefault();
if (isSubmitting || !form) return;
setSubmitting(true);
form.submit(
'/post',
{},
(status, data) => {
console.log('Response:', status, data);
setSubmitting(false);
},
(errors) => {
console.log(errors);
setSubmitting(false);
}
);
};
return (
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
onCustomSubmit={customHandling}
>
<TextField name='textField' validations={['required']} />
<button type='submit'>Submit</button>
</VGSCollectSession>
);
};Use submit.api: 'tokenization' to tokenize the collected fields against the vault. Per-field tokenization options are configured via the tokenization prop on each field.
import { VGSCollectSession } from '@vgs/collect-js-react';
const { CardNumberField, CardExpirationDateField, CardSecurityCodeField, TextField } = VGSCollectSession;
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
submit={{ api: 'tokenization' }}
onSubmitCallback={(status, resp) => {}}
onErrorCallback={(errors) => {}}
>
<TextField
name='textField'
validations={['required']}
tokenization={{ format: 'UUID', storage: 'PERSISTENT' }}
/>
<CardNumberField name='card-number' />
<CardExpirationDateField
name='card-expiration-date'
validations={['required', 'validCardExpirationDate']}
yearLength={2}
tokenization={{ format: 'UUID', storage: 'PERSISTENT' }}
/>
<CardSecurityCodeField
name='card-security-code'
validations={['required', 'validCardSecurityCode']}
/>
<button type='submit'>Submit</button>
</VGSCollectSession>Supported tokenization.format values: UUID, RAW_UUID, NUM_LENGTH_PRESERVING, PFPT, FPE_ACC_NUM_T_FOUR, FPE_ALPHANUMERIC_ACC_NUM_T_FOUR, FPE_SIX_T_FOUR, FPE_SSN_T_FOUR, FPE_T_FOUR.
Supported tokenization.storage values: PERSISTENT, VOLATILE.
VGSCollectSession integrates with the VGS Card Management Platform. Both create-card and update-card flows are supported through the submit prop.
Minimum CollectJS version: 3.2.2.
CMP sessions require:
formId— Collect session form id provisioned for CMP.authHandler— a string OAuth access token, or a function returning a token (or{ token }). The token is resolved on every submit. Resolve it on your backend rather than embedding it in the client bundle.
import {
VGSCollectSession,
VGSCollectVaultEnvironment
} from '@vgs/collect-js-react';
const { CardholderField, CardNumberField, CardExpirationDateField, CardSecurityCodeField } = VGSCollectSession;
const getAccessToken = async (): Promise<string> => {
const response = await fetch('/api/access-token');
const data = await response.json();
if (!response.ok || typeof data.access_token !== 'string') {
throw new Error(data.error_description || data.error || 'Failed to fetch access token');
}
return data.access_token;
};
const fallbackConfiguration = {
cardAttributes: {
enable: true,
parameters: ['card_brand', 'card_type', 'product_name']
}
};
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
formId='<collect_form_id>'
configuration={fallbackConfiguration}
authHandler={getAccessToken}
stateCallback={(state) => {}}
onErrorCallback={(errors) => {}}
onGetCardAttributesSuccess={(resp) => {}}
onGetCardAttributesError={(errors) => {}}
onSubmitCallback={(status, resp) => {}}
submit={{
api: 'cmp',
operation: 'createCard',
submitParameters: {
data: {
cardholder: {
address: {
address1: '123 Main St',
address2: 'Suite 456',
city: 'LA',
region: 'CA',
postal_code: '12345',
country: 'USA'
}
}
}
}
}}
>
<CardholderField defaultValue='John Doe' />
<CardNumberField />
<CardExpirationDateField defaultValue='12 / 38' />
<CardSecurityCodeField defaultValue='123' />
<button type='submit'>Submit</button>
</VGSCollectSession>;Use operation: 'updateCard' and pass the target card id via params.cardId. Typically only the expiration date and security code are collected for an update flow.
const { CardExpirationDateField, CardSecurityCodeField } = VGSCollectSession;
<VGSCollectSession
vaultId='<vault_id>'
environment='sandbox'
authHandler={getAccessToken}
onSubmitCallback={(status, resp) => {}}
onErrorCallback={(errors) => {}}
submit={{
api: 'cmp',
operation: 'updateCard',
params: { cardId: 'CRDe4CxWRkZZYZ8cGumTbESMP' }
}}
>
<CardExpirationDateField />
<CardSecurityCodeField />
<button type='submit'>Submit</button>
</VGSCollectSession>;CMP sessions emit BIN-lookup events as the user types a card number. Subscribe to them via the session props:
<VGSCollectSession
/* …other CMP props… */
onGetCardAttributesSuccess={(resp) => {
// { card_brand, card_type, product_name, ... }
}}
onGetCardAttributesError={(errors) => {}}
>
{/* fields */}
</VGSCollectSession>VGSCollectForm wraps VGSCollect.create() and is kept for backward compatibility. New integrations should prefer VGSCollectSession.
import { VGSCollectForm } from '@vgs/collect-js-react';
const { CardNumberField, CardExpirationDateField, CardSecurityCodeField } = VGSCollectForm;
const App = () => (
<VGSCollectForm
vaultId='<vault_id>'
environment='<environment>'
action='/post'
submitParameters={{ headers: { myHeader: 'MyHeader' } }}
onUpdateCallback={(state) => {}}
onSubmitCallback={(status, data) => {}}
onErrorCallback={(errors) => {}}
>
<CardNumberField name='card-number' validations={['required', 'validCardNumber']} showCardIcon />
<CardExpirationDateField name='exp-date' validations={['required', 'validCardExpirationDate']} yearLength={2} />
<CardSecurityCodeField name='cvv' validations={['required', 'validCardSecurityCode']} />
</VGSCollectForm>
);| Property | Description |
|---|---|
vaultId |
A string value beginning with tnt. |
environment |
sandbox | live | live-eu-1 | live-ap-1. |
action |
Endpoint for the HTTP request. |
submitParameters? |
HTTP request configuration. Pass submitParameters.createCard to integrate with the CMP create-card flow. |
tokenizationAPI? |
When true, the form calls form.tokenize() instead of form.submit() on submit. |
onUpdateCallback? |
Returns the form state. |
onSubmitCallback? |
Returns status and response data. |
onErrorCallback? |
Returns validation / submit errors. |
onCustomSubmit? |
Replace the default submit handler. |
cname? |
Custom CNAME the request will be submitted to. |
routeId? |
Inbound Route ID. |
Legacy CMP create-card example:
<VGSCollectForm
vaultId='<vault_id>'
environment='sandbox'
submitParameters={{
createCard: {
auth: '<vgs_auth_token>', // or: async () => '<vgs_auth_token>'
data: {
cardholder: {
name: 'Cardholder Name',
company: 'A Corp, LLC',
address: {
address1: '123 Main St',
city: 'LA',
region: 'CA',
postal_code: '12345',
country: 'USA'
}
}
}
}
}}
onSubmitCallback={(status, data) => {}}
onUpdateCallback={(state) => {}}
>
<CardholderField />
<CardNumberField />
<CardExpirationDateField />
<CardSecurityCodeField />
</VGSCollectForm>The runnable apps now live under examples/:
examples/
demo/
compat/examples/demo is the full demo app and covers every supported flow:
| Page | Source | Demonstrates |
|---|---|---|
| Basic | Basic.tsx |
All field types, field event handlers, proxy submit. |
| Custom Payload | CustomPayload.tsx |
data function, async headers, non-sensitive inputs. |
| Tokenization API | Tokenization.tsx |
submit: { api: 'tokenization' }, per-field tokenization. |
| Submit Handling | SubmitHandling.tsx |
onCustomSubmit, useVGSCollectFormInstance. |
| Card Management (Create Card) | Cmp.tsx |
CMP createCard, authHandler, card attributes events. |
| Card Management (Update Card) | CmpUpdateCard.tsx |
CMP updateCard with params.cardId. |
examples/compat contains minimal React compatibility fixtures for 16, 17, 18, and 19.
To run them from the repository root:
yarn installConfigure the demo env file:
cp examples/demo/.env.example examples/demo/.envThen edit examples/demo/.env:
VITE_VAULT_ID=<vault_id>
VITE_ENVIRONMENT=<env>
VITE_COLLECT_VERSION=<collect_version>
VITE_FORM_ID=<collect_form_id>
# Required only for CMP flows — used by the Vite dev server to mint an access token at /api/access-token.
VGS_AUTH_URL=https://auth.verygoodsecurity.com/auth/realms/vgs/protocol/openid-connect/token
VGS_CLIENT_ID=<client_id>
VGS_CLIENT_SECRET=<client_secret>Start the full demo app:
yarn demo:devOpen http://localhost:3000/.
Build the demo app:
yarn demo:buildyarn start and yarn example:dev are kept as aliases for yarn demo:dev.
If you want to rebuild the published library in watch mode while working on the demo:
yarn dev:libBuild the React compatibility fixtures:
yarn compat:buildRun the browser compatibility checks for all supported React fixtures:
yarn compat:test:uiRun a single browser compatibility check:
yarn compat:test:ui:react19Run a React compatibility fixture locally:
yarn compat:dev:react16
yarn compat:dev:react17
yarn compat:dev:react18
yarn compat:dev:react19Ports:
demo: 3000
react16 fixture: 3016
react17 fixture: 3017
react18 fixture: 3018
react19 fixture: 3019External:
If you have any questions please reach out to support or open an issue here.
This project is licensed under the MIT license. See the LICENSE file for details.