User interface
The starter-kit comes pre-packaged with an example opinionated user interface. It consists of a simple Next.js/React application, configured with:
- Tailwind CSS (opens in a new tab) for standardised styling
- Shadcn UI (opens in a new tab) as a component library
- Zustand (opens in a new tab) & Immer (opens in a new tab) as middleware for state management
- Dependency on the
chain
package, which contains your application chain
Wallet compatibility
The example user interface is compatible with the Auro wallet (opens in a new tab). This is possible thanks to an app-chain level module called the AuroSigner, which delegates the responsibility of signing transactions to the auro wallet.
Configuration of the AuroSigner is done implicitly behind the scenes as part of the ClientAppChain in client.config.ts.
If you'd like to use a different wallet, you'll have to implement a compatible signer module, following the Signer interface:
export interface Signer extends AppChainModule<unknown> {
sign: (signatureData: Field[]) => Promise<Signature>;
}
In automated tests we use a special version of the signer known as the InMemorySigner, that does not require a browser wallet to be installed.
Top-level await
O1JS uses top-level await, which does not play nicely with Next.js's app router due to the built-in server side rendering. However there's a simple solution - loading our react components dynamically with ssr disabled.
Top-level await incompatibility is the reason for splitting both layout and page into two files, one with a -dynamic suffix and one without.
You can learn more about lazy loading react components here (opens in a new tab).
Can you think of a better solution to this problem? Let us know!
Features
The example user interface comes with a few features out of the box, with the intention of showcasing various interaction points between the UI and the app-chain. These features are:
- Managing the app-chain client
- Connecting the compatible browser wallet
- Polling for new blocks from the app-chain
- Fetching balances for the connected wallet, every time a new block is received
- Interacting with the app-chain's "faucet" (Balances.addBalance)
- Handling transaction notifications, by observing transaction statuses in the latest block.
Architectural separation
All of the features above are implemented with a clear separation of concerns, by separating the code into three distinct layers:
- Data layer (Zustand stores, interaction with the app-chain, data fetching)
- Compositional layer (React containers, connecting the data layer to the presentational layer)
- Presentational layer (React components, responsible for how the data is displayed)
App-chain client
Interacting with the locally hosted app-chain from the UI is done through the ClientAppChain, same as in the client interaction documentation. Both the transaction API and the query API is available and preconfigured to work with your app-chain's runtime definition.
Keep in mind that the ClientAppChain uses the GraphQL API of the locally hosted sequencer, so you'll have to run both at the same time.
Using a different UI framework
If you would like to use a different UI framework/stack than the one provided in the starter-kit, you totally can! However there's quite a few things you have to keep in mind as far as configuration goes. The current UI setup should give you enough information to switch to a different framework.