DApp UI
This section presents how to create and setup a DApp's User Interface (UI) project using React and Beacon's DApp/wallet interaction.
Technical stack
App Framework
Any framework is suitable to create a DApp application as long as a Tezos library is available for it. We present here how to create a web DApp with React.
The choice of the React framework depends on the objective and the size of the application; typically a framework like Next.js is suited for large applications with needs for server side rendering. We are choosing create-react-app for its simplicity to create simple client-side & single-page applications: it installs and configures the whole pack of required bricks (webpack, babel, ...)
Language
It is strongly recommended to use TypeScript language as it will greatly shorten and ease up the development cycle. Its typed aspect makes that many errors are detected at compilation time (ie. at the time of writing code) rather than later at execution. It just doesn't make sense to developp with untyped languages when you think of it ...
Project templates configured with TypeScript are available with most UI frameworks. The following command uses create-react-app
template with TypeScript:
npx create-react-app my-dapp --template TypeScript
This creates the my-dapp
project. More information may be found here
Tezos library
While it is always possible to interact directly with the Tezos endpoint's RPC API, it is more than recommended to use a dedicated library that will wrap all services in high-level development services and take care of all the low-level Tezos protocol details.
In the context of web DApps for Tezos, the main library is Taquito:
npm install @taquito/taquito
Taquito uses official cryptographic packages (aka libraries). Some of these packages relies on nodejs packages designed to run on the back-end side (server side, not in a browser). These packages are crypto
, stream
, assert
, http
, https
, os
. As a result, the default build process fails; it is then necessary to map these packages to their front-end counterparts in the build process:
Follow instructions available here to solve build issues.
As a comment, create-react-app
uses webpack (version 5) to bundle all resources as static web assets. It is then necessary to use the react-app-rewired
package as described in instructions above, to be able to provide a customized webpack configuration without ejecting the react app.
Wallets
A DApp needs to interact with a wallet to sign operations (transfers, calls to a smart contract, ...). Many wallets are available on Tezos (Temple, Kukai, Umami, ...). It is common practice to interact with them all via Beacon that implements the interaction standard TZIP-10 between a wallet and a dApp, as it greatly reduces the integration effort with wallets.
npm install @taquito/beacon-wallet @airgap/beacon-sdk
The main drawback of Beacon is the lack of control over the UI elements (typically the wallet selection popup), which can be a no-go if you want a tight control of the DApp L&F. In that case, each wallet needs to be integrated separately.
A plug-and-play constate context for Beacon services connect
and disconnect
is available here
.
Contract binding
When interacting with a contract (read and write), it is strongly recommended to use its generated TypeScript binding, that is a typed high-level TypeScript interface. It greatly reduces the effort to call a contract, read its storage and the number of runtime errors, as the compilier and LSP guides you through the contract interface.
TypeScript bindings may be obtained with the following Completium CLI command:
completium-cli generate binding-dapp-ts mycontract.arl > mycontract.ts
Bindings generation is also available for Michelson (.tz) files.
The generated binding interface relies on two packages:
npm install @completium/dapp-ts @completium/archetype-ts-types
Contexts
With React applications, it is strongly recommended to setup contexts for application data (settings, UI states, ...) in read/write modes with a dedicated package like constate or redux. This is preventing from awkward spaghetti code of passing components states and data through large hierarchy of components.
The DApp example presented here is using constate for its lightweight aspect.
npm install constate
Taquito and Beacon must be singletons, hence there are wrapped as contexts (with constate) to make them available to UI components. The same stands for the contract bindings.
The main 4 blockchain-related contexts are provided as plug-and-play code:
Contexts
Architecture
Schema below illustrates the module and package architecture of the DApp and their interactions:
- Changes in
Contexts
data automatically redrawUI
components that use them (React + constate hook mecanism) Contexts
andUI
interact with contract viaBinding
Contexts
uses Beacon's services to connect to a wallet- Taquito's Tezos toolkit uses Beacon as transaction signer
Binding
uses@completium/dapp-ts
package services to interact with blockchain@completium/dapp-ts
uses@completium/event-listener
to listen to emitted events
File structure
The typical file structure of the react project is presented below:
├── README.md
├── config-overrides.js
├── package.json
├── tsconfig.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.test.tsx
├── App.tsx
├── index.css
├── index.tsx
├── react-app-env.d.ts
├── reportWebVitals.ts
├── setupTests.ts
├── bindings
│ └── ...
├── components
│ └── ...
├── routes
│ └── ...
└── contexts
├── Beacon.tsx
├── Taquito.tsx
├── Contract.tsx
├── Settings.tsx
└── ...
Note that 4 directories are created under src
:
bindings
: generated contracts bindingsroutes
: page UI components managed by a route manager (for examplereact-router-dom
)components
: other UI componentscontexts
: hooks providers