This repository provides libraries for building applications for Salesforce's Lightning Experience in Go using SLDS-styled masc components, which follow the Elm Architecture.
Thunder is made up of these masc components, a thunder
LWC for running the
compiled wasm applications, a lightweight API designed to mirror the REST
API, and a CLI to make development a joy.
Repository Structure:
. (repo root)
├ cmd/thunder/ CLI source (Cobra commands: serve, deploy)
│ ├ main.go CLI implementation
│ ├ main_test.go CLI command tests
├ salesforce/ embedded metadata templates
│ ├ classes/ Apex classes
│ └ lwc/ LWC wrappers (`go`, `thunder`)
├ components/ MASC components for Thunder apps
├ api/ REST proxy for WASM apps, UI API metadata (GetObjectInfo, GetPicklistValuesByRecordType), and Record API for convenient field access (StringValue, Value)
└ examples/ example Thunder applications
├ thunderDemo/ main demo app showcasing all components
└ validation/ comprehensive form validation example
Key parts:
- thunder LWC (
c:thunder
):- Loads a Go WASM app as static resource, injects global API functions, and runs the app.
- Exposes the
recordId
from Lightning record pages to Go WASM code viaglobalThis.recordId
.
- Thunder SLDS Components (
components/
):- Go library offering SLDS-styled Masc components like
Button
,DataTable
,Grid
,Textarea
, andStencil
.
- Go library offering SLDS-styled Masc components like
- Apex GoBridge (
salesforce/classes/GoBridge.cls
):@AuraEnabled callRest(...)
to map REST API calls to Apex.
- thunder CLI (
cmd/thunder
):- Command-line tool to serve a Thunder app while in development and, and to
build and deploy it to a Salesforce org.
Example Applications (
examples/
): - thunderDemo: Demonstrates all Thunder components in a Go WASM app, organized into Actions, Data, ObjectInfo, and Layout tabs. Showcases component-only development without direct SLDS classes or elem usage.
- validation: Comprehensive form validation example with real-time error handling, demonstrating ValidationState and validated components.
- Command-line tool to serve a Thunder app while in development and, and to
build and deploy it to a Salesforce org.
Example Applications (
Getting Started:
- Install dependencies:
- Go 1.24+ (with WASM support)
- Force CLI
- Run the thunderDemo app locally:
This compiles
$ force login $ thunder serve ./examples/thunderDemo
examples/thunderDemo/main.go
and starts a web server to serve the app. - Deploy to Salesforce using
thunder deploy ./examples/thunderDemo --tab
- Click Fetch Accounts to see a data table rendered from your Thunder app.
Thunder provides a comprehensive set of SLDS-styled components for building Lightning Experience applications:
TextInput
: Single-line text input with label and validation stylingTextarea
: Multi-line text input for longer content (e.g., addresses, descriptions)Select
: Dropdown selection with picklist optionsDatepicker
: Date input with SLDS calendar stylingCheckbox
: Boolean input with proper labelingRadioGroup
: Multiple choice selection with radio buttonsValidatedTextInput
,ValidatedTextarea
, etc.: Form components with built-in validation state management
Grid
&GridColumn
: Responsive grid system for flexible layoutscomponents.Grid( components.GridColumn("1-of-2", /* first column content */), components.GridColumn("1-of-2", /* second column content */), components.GridColumn("1-of-1", /* full-width content */), )
CenteredGrid
: Grid with center alignment for loading states and centered contentCard
: Content containers with headers and proper spacingPage
&PageHeader
: Page-level layout with consistent stylingModal
: Dialog overlays for secondary workflowsContainer
: Basic layout wrapper to avoid direct element usageSpacer
: Flexible spacing container with margin/padding optionsMarginTop
,MarginBottom
,PaddingHorizontal
, etc.: Semantic spacing components
DataTable
: Feature-rich data tables with sorting and actionsLookup
: Search and selection for related records
Button
: Action buttons with variant styling (Neutral, Brand, Destructive)LoadingButton
: Button with built-in spinner and disabled stateBadge
: Status indicators and labelsBreadcrumb
: Navigation hierarchy displayIcon
: SLDS icon integrationProgressBar
: Progress indication for long-running operationsSpinner
: Loading indicators in multiple sizesLoadingSpinner
: Centered loading spinner for containersStencil
: Skeleton placeholders for loading statesTabs
: Tabbed content organizationToast
: Notification messages
Text
: Styled text with size variants (Small, Regular, Large)Paragraph
: Paragraph elements with text stylingHeading
: Heading elements (H1/H2/H3) with semantic sizing (Small, Medium, Large)
- Complete Abstraction: No direct SLDS classes or masc elements required in application code
- Semantic APIs: Type-safe spacing, sizing, and styling options
- Consistent Spacing: Semantic spacing components (Spacer, MarginTop, etc.)
- Responsive Design: Grid system adapts to different screen sizes
- Accessibility: Full SLDS accessibility compliance
- Event Handling: Clean event binding with Go functions
- Type Safety: Strongly typed APIs for reliable development
- Loading States: Built-in support for loading spinners and disabled states
Thunder components provide complete abstraction from SLDS classes and DOM elements:
// Instead of using elem.Div with SLDS classes
elem.Div(
masc.Markup(masc.Class("slds-m-top_medium", "slds-align_absolute-center")),
components.Spinner("medium"),
)
// Use semantic layout components
components.MarginTop(components.SpaceMedium,
components.LoadingSpinner("medium"),
)
// Complex layouts with semantic spacing
func (m *AppModel) renderPatientForm(send func(masc.Msg)) masc.ComponentOrHTML {
return components.Page(
components.PageHeader("Patient Information", "Enter patient details"),
components.Card("Patient Details",
components.Grid(
components.GridColumn("1-of-2",
components.ValidatedTextInput("First Name", m.firstName, "",
components.ValidationState{
Required: true,
HasError: m.hasError("firstName"),
ErrorMessage: m.errors["firstName"],
},
func(e *masc.Event) {
send(firstNameMsg(e.Target.Get("value").String()))
},
),
),
components.GridColumn("1-of-2",
components.ValidatedTextInput("Last Name", m.lastName, "",
components.ValidationState{Required: true},
func(e *masc.Event) {
send(lastNameMsg(e.Target.Get("value").String()))
},
),
),
),
// Loading button with built-in spinner - conditional rendering with masc.If
masc.If(m.isSubmitting,
components.LoadingButton("Saving...", components.VariantBrand),
),
masc.If(!m.isSubmitting,
components.Button("Save", components.VariantBrand, func(e *masc.Event) {
send(saveMsg{})
}),
),
),
)
}
Thunder provides validated form components that handle error states, required field validation, and help text. Each validated component includes:
- Error State Management: Red styling for validation errors
- Required Field Indicators: Asterisk (*) for required fields
- Help Text: Descriptive text below form fields
- Real-time Validation: Immediate feedback on user input
ValidatedTextInput
: Text input with validation stateValidatedTextarea
: Multi-line text with validationValidatedSelect
: Dropdown selection with validationValidatedDatepicker
: Date input with validation
All validated components use the ValidationState
struct:
type ValidationState struct {
HasError bool // Show error styling
Required bool // Show asterisk indicator
ErrorMessage string // Error text to display
HelpText string // Help text below field
}
validationState := components.ValidationState{
HasError: len(m.email) > 0 && !isValidEmail(m.email),
Required: true,
ErrorMessage: "Please enter a valid email address",
HelpText: "We'll use this to send important updates",
}
components.ValidatedTextInput("Email", m.email, "user@example.com", validationState, func(e *masc.Event) {
send(emailChangedMsg(e.Target.Get("value").String()))
})
The examples/
directory contains complete Thunder applications demonstrating different patterns:
The main demonstration app showcasing all Thunder components across four tabs:
- Actions: Buttons, badges, icons, and date pickers
- Data: Interactive data table with filtering, pagination, and controls
- ObjectInfo: Salesforce metadata display using the UI API
- Layout: Grid system demonstration
Features component-only development with no direct SLDS classes or masc elements.
thunder serve ./examples/thunderDemo
Comprehensive form validation example demonstrating:
- Real-time field validation with
ValidationState
- Required field indicators and error messages
- Loading states with
LoadingButton
- Semantic spacing with
MarginTop
andSpacer
Shows how to build robust forms using only Thunder components.
thunder serve ./examples/validation
Both examples are complete Go modules that can be run independently with thunder serve
or deployed with thunder deploy
.
Thunder provides a CLI with two subcommands, serve
and deploy
, for local development and deployment of Go WASM apps on Salesforce.
go install github.com/octoberswimmer/thunder/cmd/thunder@latest
thunder serve [dir] --port PORT # build & serve locally
5A62
(defaults to current dir)
thunder deploy [dir] [--tab] # deploy app to Salesforce org (defaults to current dir)
--port, -p
: Port to serve on (default8000
)
thunder serve
:
- Builds the app in dev mode (
GOOS=js GOARCH=wasm -tags dev
). - Serves on
http://localhost:PORT
, auto-rebuilds on file changes. - Proxies
/services/...
REST calls to your Salesforce org via CLI auth. - Opens your default browser to the served app URL.
The CLI watches Go source files (.go
, go.mod
, go.sum
) and automatically rebuilds the WASM bundle on changes. Refresh the browser to load the latest build.
API REST requests (via /services/
) are automatically proxied through your active Salesforce CLI session. Be sure to run force login
beforehand.
--tab, -t
: Also include a CustomTab in the deployment and open it for the app
thunder deploy
:
- Builds a production WebAssembly bundle.
- Packages metadata (static resource, Apex classes, LWC wrappers, app LWC, and optional CustomTab) in-memory.
- Generates
package.xml
(includes CustomTab if requested) and deploys all metadata via your CLI session. - With
--tab
, adds a CustomTab to the package, deploys it, and opens/lightning/n/<app>
in your browser.