canton-network-docs
Frontend Development
Frontend Development - Canton Network Docs
Skip to main content
The shared
If the backend API changes, the frontend build breaks rather than failing silently at runtime.
The
Whether you use DAR-generated types depends on your architecture:
Each domain has its own store under
Because the backend handles all ledger translation, the frontend works with plain JSON objects. Fields like
The
The
For faster iteration, run the Vite dev server directly against a running LocalNet backend:
The Vite dev server proxies API requests to the backend. The proxy configuration in
The
You’ll add a comment list and comment form to the licenses view, following the same store/view patterns that cn-quickstart uses for licenses.
This runs
The structure mirrors
Add an effect that polls for comments when a license’s comment panel is open. This follows the same 5-second
Add a “Comments” toggle button in each license row’s actions column, and render the comment panel below the license table. The panel shows existing comments and a form for new ones:
The
Or for faster frontend iteration with hot reload:
Open the app, navigate to the Licenses page, click the “Comments” button on a license, and try posting a comment. The comment should appear in the list within 5 seconds (or immediately after the POST completes, since
The frontend is the user-facing layer of your Canton application. This page uses cn-quickstart as a running example. cn-quickstart is a full-stack reference application that implements a software licensing workflow on Canton Network. The cn-quickstart frontend is a React application built with TypeScript and Vite. It communicates with the backend over HTTP using types generated from a shared OpenAPI schema. The patterns shown here — connecting to the backend, displaying contract data, handling authentication — apply to any Canton frontend, but the code samples are drawn directly from cn-quickstart so you can see them in a working context.Documentation Index
Fetch the complete documentation index at: https://docs.canton.network/llms.txt
Use this file to discover all available pages before exploring further.
Connecting to the Backend
In cn-quickstart, the frontend does not talk to the Ledger API directly — all ledger interactions go through the backend’s REST endpoints. Canton does provide a JSON API that frontends can use for direct ledger access, but the cn-quickstart architecture routes everything through the backend for separation of concerns. The API client is configured inapi.ts using the openapi-client-axios library, which reads the OpenAPI schema and produces a typed HTTP client:
common/openapi.yaml defines every endpoint, request body, and response shape. The openapi-client-axios library generates a typed Client interface from this spec at build time, so every API call in the frontend is type-checked against the backend’s contract:
TypeScript Code Generation
The cn-quickstart frontend generates its TypeScript types from the OpenAPI spec using thegen:openapi script in package.json:
build script runs type generation before compilation, so the TypeScript types always match the OpenAPI schema.
Separately, dpm codegen-js generates TypeScript types from your compiled DAR file. These types mirror your Daml templates, choices, and data types:
- Fully mediated (cn-quickstart default) — The frontend uses OpenAPI-generated types from the backend’s REST schema. The Daml-generated TypeScript types are not needed in the frontend because the backend translates between ledger concepts and REST DTOs.
- Direct ledger access via JSON API — The frontend submits commands through the JSON API using the Daml-generated TypeScript bindings. This gives tighter integration with the ledger but requires the frontend to handle party IDs, contract IDs, and command submission directly.
Application Structure
The cn-quickstart frontend uses React Context providers for state management.App.tsx composes them at the top level:
stores/ that wraps a React Context with the API calls and state for that domain. The stores use the typed Client from the OpenAPI schema for all backend communication.
Displaying Contract Data
The frontend fetches contract data from the backend’s GET endpoints and renders it in React components. ThelicenseStore manages license state and API calls:
LicensesView.tsx consumes this store and renders the data with periodic polling to keep the UI current:
contractId, expiresAt, and licenseNum appear as simple strings and numbers in the License type — not ledger-specific types.
Exercising Choices via the Backend
When the user takes an action (renew a license, expire a license), the frontend posts to the backend’s REST API. Each request includes a unique command ID that the backend passes to the Ledger API for deduplication. From the license store:generateCommandId() utility produces a UUID using the browser’s crypto.randomUUID() API. The backend forwards this ID to the Ledger API, which uses it to prevent duplicate command submission if the user retries an action.
Authentication
Canton applications typically use OAuth2 / OpenID Connect (OIDC) for user authentication. On LocalNet, cn-quickstart uses Keycloak as the identity provider. The backend handles the OAuth2 flow, and the frontend manages session state through theuserStore:
AuthenticatedUser type (from the OpenAPI spec) includes the user’s name, whether they’re an admin, and their wallet URL. Components use this to conditionally render admin-only features and to link to the Splice wallet. The backend handles all authorization decisions — the frontend is responsible only for checking whether the user is logged in and displaying the appropriate UI.
Wallet Integration
Applications that involve Canton Coin payments integrate with a wallet component in the frontend. In cn-quickstart, the license renewal flow triggers a payment through the Splice wallet system:- The user clicks “Renew License” in the UI
- The frontend calls
client.renewLicense(), which posts to the backend - The backend exercises the
License_Renewchoice, creating aLicenseRenewalRequeston the ledger that implements the SpliceAllocationRequestinterface - The Splice wallet detects the allocation request and creates an
AppPaymentRequestfor the user to approve - Once payment is confirmed, the provider calls
completeLicenseRenewal()to create the renewed license
LicensesView tracks renewal state by polling. Each license object includes its renewalRequests array, and the UI shows the count of pending and accepted renewals. The user’s wallet URL comes from the AuthenticatedUser object and is used to link to the Splice wallet for payment approval.
Development Workflow
When developing the frontend iteratively:vite.config.ts routes /api, /login, and /oauth2 paths to the backend:
ViteYaml plugin allows importing the OpenAPI YAML file directly as a JavaScript module, which is how api.ts loads the schema at build time.
Exercise: Add License Comments UI
This exercise builds on the backend exercise in Backend Development. Complete that first — you need the
LicenseComment Daml template, the OpenAPI endpoints, and the backend implementation before the frontend can use them.Step 1: Regenerate TypeScript Types
The backend exercise addedLicenseComment, AddCommentRequest, and two new endpoints to openapi.yaml. Regenerate the frontend types so the typed client picks them up:
openapicmd typegen against the shared openapi.yaml and overwrites src/openapi.d.ts. After this, your Client type will include listLicenseComments() and addLicenseComment() methods with the correct parameter and return types.
Step 2: Create a Comment Store
Create a new store atquickstart/frontend/src/stores/commentStore.tsx. Follow the same Context + Provider pattern used by licenseStore.tsx:
licenseStore.tsx: a React Context holds the state, each API call is wrapped with withErrorHandling (which displays toast messages on HTTP errors) and useCallback, and the typed Client provides compile-time checking against the OpenAPI spec. The addComment function generates a command ID for deduplication — the same pattern expireLicense and renewLicense use.
Register the provider in App.tsx alongside the existing providers:
Step 3: Add the Comment UI
Add a comment section toLicensesView.tsx. You’ll need to import the comment store and add some local state:
setInterval pattern LicensesView already uses for the license list:
showComments state tracks which license’s contract ID is currently expanded. The formatDateTime utility is already imported in LicensesView for the expiration column. The handleAddComment handler calls addComment from the store, then clears the input.
Step 4: Build and Test
Rebuild both the backend (to pick up the OpenAPI changes and new Java code) and the frontend:addComment calls fetchComments after success).
Next Steps
- Canton Coin and Traffic — How CC and traffic affect your application
- Backend Development — The backend that the frontend communicates with
- cn-quickstart frontend source — Full working frontend implementation