To install dependencies:
npm installTo run:
npm run devIf you just need to develop a component outside of its context within a workflow, you can use Storybook:
npm run storybook
If you need to test your component within the larger context of a workflow, you'll need to setup Zenpayroll + GWS-Flows.
- Follow setup instructions for Zenpayroll and start local dev server
- In another tab run the following commands in Zenpayroll directory(these generate necessary partner account for
gws-flows) :
bundle exec rails runner "DevAccountCreator.new.create_dev_accounts" rake partners_api:dev_setup_for_gws_onboarding - In another tab run the following commands in Zenpayroll directory(these generate necessary partner account for
- Follow setup instructions in the readme for GWS Flows
- in SDK, Run
npm run dev:setup; if that fails, follow the instructions below to setup manually:- Run
npm link ../gws-flows/node_modules/reactin sdk folder - this is needed to avoid duplicate react instances in local development due to using npm/yarn link - In GWS-Flows project folder, run
yarn link -r ../embedded-react-sdk
- Run
- In SDK, run
npm run dev
Now your local changes appear in GWS Flows.
To see the SDK running in GWS Flows, visit it locally and choose React SDK (New company) or React SDK (Company Onboarded) under Select a Type and click Create Demo
Components and Flows will be shown at the top of the page in a nav. Company Components will automatically appear as they are added.
Select a Flow or Component to view it
Translations are stored in /src/i18n.
The are broken out by locale (e.g. en), then by namespace, which is usually the name of the component they are used in. The source of truth is the en locale - the rest will be autogenerated by translation service.
After writing the new translations, run npm run interface to generate the types.
In your components, first use useI18n hook, which takes in component namespace and loads appropriate resource file into dictionary. After that use the useTranslation hook to access the translations in that component and any child components.
Follow these conventions when creating or modifying translation keys:
Use camelCase for all translation key names:
{
"pageTitle": "Federal Tax Information",
"federalEinLabel": "Federal EIN",
"federalEinDescription": "Your company's Federal Employer Identification Number",
"continueCta": "Continue"
}Use consistent suffixes to indicate the purpose of each key:
Cta- Call-to-action buttons (e.g.,submitCta,continueCta,cancelCta)Label- Form field labels (e.g.,firstNameLabel,emailLabel)Description- Help text or descriptions (e.g.,federalEinDescription)Title- Page or section headings (e.g.,pageTitle,sectionTitle)Placeholder- Input placeholders (e.g.,emailPlaceholder)Error- Error messages (e.g.,requiredFieldError,invalidEmailError)
Group related keys using nested objects for better organization:
{
"validations": {
"firstName": "First name is required",
"lastName": "Last name is required",
"email": "Email address is required and must be valid",
"address": {
"street1": "Street address is required",
"city": "City is required",
"state": "State is required"
}
},
"labels": {
"openMenu": "Open menu",
"menuLabel": "Menu"
},
"alerts": {
"progressSaved": "Your progress has been saved",
"errorEncountered": "There was a problem with your submission"
}
}Common grouping patterns:
validations- Form validation error messageslabels- Accessibility labels and field labelsalerts- Alert and notification messagesicons- Icon accessibility labelstable- Table-related labels (column headers, actions)
Only use snake_case when required by external contracts:
- API Enum Values - When keys match backend API responses:
{
"onboardingStatus": {
"admin_onboarding_incomplete": "Admin-onboarding Incomplete",
"self_onboarding_invited": "Self-onboarding: Invited"
}
}- i18next Pluralization - Required by i18next for plural forms:
{
"priority_one": "{{count}}st",
"priority_two": "{{count}}nd",
"priority_few": "{{count}}rd",
"priority_other": "{{count}}th"
}- Programmatic Identifiers - When used as flow/step identifiers that match API:
{
"stepTitles": {
"add_addresses": "Add company addresses",
"federal_tax_setup": "Company federal tax information"
}
}Good:
{
"pageTitle": "Employee Profile",
"continueCta": "Continue",
"cancelCta": "Cancel",
"validations": {
"firstName": "First name is required",
"emailFormat": "Email must be valid format"
},
"labels": {
"personalInfo": "Personal Information"
}
}Avoid:
{
"page_title": "Employee Profile", // ❌ Don't use snake_case
"ContinueCTA": "Continue", // ❌ Don't capitalize suffix
"validation_first_name": "First name...", // ❌ Don't use snake_case
"first_name_validation": "First name...", // ❌ Poor grouping
"lbl_personal_info": "Personal Info" // ❌ Don't abbreviate
}Block components are focused, reusable components that serve specific functionality. They follow these patterns:
- Single Purpose: Each component should generally handle a specific task (e.g., list, form, etc.)
- Base Component: Uses BaseComponent for consistent behavior and error handling
- Compound Pattern: Exposes subcomponents (Head, List, Actions) for flexibility and composition
Flow components compose block components and other flow components together using state machines to manage transitions. They follow these patterns:
- State Management: Uses the Flow component with a state machine to handle transitions
- Component Composition: Can compose both block components (e.g., list → form) and other flow components
- Naming: Suffix with "Flow" (e.g.,
DocumentSigner,Locations)
For example, EmployeeOnboardingFlow composes both block components (profile, taxes) and other flow components (document signer) to create a complete onboarding experience.
You can run the test suite locally with the following command:
npm run testWhen creating a pull request, use the provided PR template (.github/PULL_REQUEST_TEMPLATE.md). Here are the guidelines:
Use conventional commits format for your PR title. PR titles are validated by CI and used for automatic semantic versioning.
Note: This PR title validation works alongside the existing commitlint check. Commitlint validates individual commit messages (enforced via husky pre-commit hook), while this workflow validates the PR title which is used for squash merge commits and version bumping.
PR titles determine how the package version is bumped on merge, following semver.org specification:
During 0.x.x (pre-1.0 development):
| Commit Type | Version Bump | When to Use |
|---|---|---|
feat |
MINOR (0.1.0 → 0.2.0) | New features or functionality |
fix |
PATCH (0.1.0 → 0.1.1) | Bug fixes |
feat! or fix! (with !) |
MINOR (0.1.0 → 0.2.0) | Breaking changes* |
docs, chore, refactor, test, ci, style, perf, build, revert |
No bump | Non-functional changes |
*Per semver spec, during 0.x.x the API is considered unstable, so breaking changes bump MINOR instead of MAJOR. The jump to 1.0.0 will be an intentional decision when we're ready to declare a stable API.
feat: add new component- New feature → MINOR bumpfix: resolve issue with form validation- Bug fix → PATCH bumpfeat!: redesign JSX component props- Breaking change → MINOR bump (during 0.x.x)chore: update dependencies- Maintenance → no version bumprefactor: simplify state machine logic- Code refactoring → no version bumpdocs: update README- Documentation → no version bump
For work tied to a Jira ticket, include the ticket in the scope so the title still follows conventional commits:
feat(SDK-123): add payroll blocker alerts- MINOR bumpfix(SDK-456): correct date formatting- PATCH bump
- Summary: Brief description of what the PR does and why
- Changes: Briefly list only the most important changes (high-level only; do not enumerate every file-level change)
- Demo: Screenshots or screen recordings showing the changes (when applicable)
- Related: Links to Jira tickets, Figma designs, or related PRs
- Testing: Instructions for reviewers to test the changes
- Keep PRs focused on a single concern when possible
- Include visual demos (screenshots/videos) for UI changes
- Link to relevant Jira tickets using the format
[SDK-XXX](https://gustohq.atlassian.net/browse/SDK-XXX) - Add Storybook stories for new components
- Include unit tests for new functionality
- Run
npm run testandnpm run lintbefore submitting
All commits must follow commitlint enforced format: type(scope?): subject #scope is optional; multiple scopes are supported (current delimiter options: "/", "\" and ",")
Following types are currently defined:
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test'
Read more about conventional commits here
You can find documentation on building with the Gusto Embedded React SDK in the docs directory within this repo here. Documentation is also hosted live alongside the Gusto Embedded API docs here.
Package versions and the changelog are automatically updated when PRs are merged to main, based on the PR title:
-
Automatic version bumping: When a PR is merged (not when it's opened), the
auto-versionworkflow reads the PR title and bumpspackage.jsonversion accordingly:feat:→ MINOR bumpfix:→ PATCH bumpfeat!:orfix!:(with!) → MINOR bump (during 0.x.x pre-release)- Other types (
docs,chore, etc.) → no version bump
-
Automatic changelog updates: The workflow also adds an entry to
CHANGELOG.mdbased on the PR title description:feat→ "Features & Enhancements" sectionfix→ "Fixes" section- Breaking changes (with
!) → "Breaking Changes" section - Other types → "Chores & Maintenance" section
Note: The changelog entry uses the description portion of your PR title (e.g.,
feat: add new componentbecomes "add new component" in the changelog). For breaking changes, consider including migration guidance in your PR description - the auto-generated changelog entry provides the "what" but the PR body can provide the "how to migrate". -
Publishing: After the version is bumped, run the
Publish to NPMGitHub action here by clickingRun workflow
If you need to manually cut a release:
- Update the
versionfield inpackage.json - Update
CHANGELOG.mdwith the changes - Run
npm iand commit the new lockfile - Run the
Publish to NPMGitHub action