Customizing the front-end
Now that you have a basic understanding of how to create, build, and deploy a simple program and are familiar with the default project files and sample front-end, you might want to start experimenting with different ways to customize the front-end user experience for your project.
This tutorial illustrates using a simple React framework to create a new front-end for the default sample program and guides you through some basic modifications to customize the interface displayed. Later tutorials expand on the techniques introduced here, but if you already know how to use CSS, HTML, JavaScript, and React or other frameworks to build your user interface, you can skip this tutorial.
Currently, you can only use Javascript to implement the front-end for your canister. This tutorial illustrates using the React framework to manage the Document Object Model (DOM) for your canister. Because React has its own custom DOM syntax, you need to modify the webpack configuration to compile the front-end code, which is written in JSX. For more information about learning to use React and JSX, see Getting started on the React website. |
Before you begin
Before starting the tutorial, verify the following:
-
You have
node.js
installed for front-end development and can install packages usingnpm install
in your project. For information about installing node for your local operating system and package manager, see the Node website. -
You have downloaded and installed the DFINITY Canister SDK package as described in Download and install.
-
You have installed the Visual Studio Code plugin for Motoko as described in Install the language editor plug-in if you are using Visual Studio Code as your IDE.
-
You have stopped any Internet Computer network processes running on the local computer.
Create a new project
To create a new project directory for your custom front-end application:
-
Open a terminal shell on your local computer, if you don’t already have one open.
-
Change to the folder you are using for your Internet Computer sample projects.
-
Check that you have
node.js
installed locally by running the following commands:which node which npm
If you don’t have
node.js
installed, you should download and install it before continuing to the next step. For information about installing node for your local operating system and package manager, see the Node website. -
Create a new project by running the following command:
dfx new custom_greeting
The
dfx new custom_greeting
command creates a newcustom_greeting
project. -
Change to your project directory by running the following command:
cd custom_greeting
Install the React framework
If you’ve never used React before, you might want to explore the Intro to React tutorial or the React website before editing the front-end code.
To install required framework modules:
-
Install the React module by running the following command:
npm install --save react react-dom
-
Install the required TypeScript language compiler loader by running the following command:
npm install --save typescript ts-loader
Modify the default configuration
For this tutorial, you need to modify the default front-end settings in the dfx.json
configuration file for your project.
To modify the default dfx.json
configuration file:
-
Open the
dfx.json
configuration file in a text editor. -
Notice that the
canisters
key includes settings for acustom_greeting_assets
canister.{ "canisters": { ... "custom_greeting_assets": { "dependencies": [ "custom_greeting" ], "frontend": { "entrypoint": "src/custom_greeting_assets/public/index.js" }, "source": [ "src/custom_greeting_assets/assets", "dist/custom_greeting_assets/" ], "type": "assets" } } }
Let’s take a look at the settings in this section.
-
Front-end assets for your project are compiled into their own canister, in this case, a canister named
custom_greeting_assets
. -
The assets canister has a default dependency on the main canister for the project.
-
The
frontend.entrypoint
setting specifies the path to a file—in this case, theindex.js
file—to use as your application entry point. If you had a different starting point—for example, a customindex.html
file—you would modify this setting. -
The
source
setting specifies the path to a directory for static assets that will be included in your assets canister when you build your project. If you had custom cascading stylesheet (CSS) or JavaScript files, you would include them in this source folder. After building the project, these assets are served from the directory specified by thefrontend.output
setting. -
The
type
setting specifies that thecustom_greeting_assets
is an asset canister rather than a program canister.
For this tutorial, change the
entrypoint
file name toindex.jsx
to enable adding HTML directly inside the React Javascript front-end."entrypoint": "src/custom_greeting_assets/public/index.jsx",
-
-
Close the
dfx.json
file to continue.
Review the default program
For this tutorial, you are going to use the default main.mo
file.
Later, you can return to this file and make changes to see how you can use the front-end to reflect changes in your program code.
To review the default program:
-
Change to the
src/custom_greeting
directory.cd src/custom_greeting
-
Open the
main.mo
file to review the default code.The default program consists of one actor, one
greet
function, and onename
argument. As you saw in Viewing the default front-end, the default front-end for this program uses a few lines of raw JavaScript to place thegreet
function and returnedname
argument a simple alert window. -
Close the
main.mo
file to continue.
Modify the front-end files
You are now ready to create a new front-end for the default program.
To prepare the front-end files:
-
Navigate back to the root of your project directory.
-
Open the webpack configuration file (
webpack.config.js
) in a text editor. -
Add the following
module
key above theplugins
section:module: { rules: [ { test: /\.(js|ts)x?$/, loader: "ts-loader" } ] },
This setting enables the program to use the
ts-loader
compiler for theindex.jsx
file. -
Create a new file named
tsconfig.json
in your project directory. -
Open the
tsconfig.json
file in a text editor, then copy and paste the following into the file:{ "compilerOptions": { "target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["ES2018", "DOM"], /* Specify library files to be included in the compilation. */ "allowJs": true, /* Allow javascript files to be compiled. */ "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ }, "include": ["src/**/*"], }
-
Save your changes and close the
tsconfig.json
file to continue. -
Change to the
src/custom_greeting_assets/public
directory. -
Open the default
src/custom_greeting_assets/public/index.js
file in a text editor and delete the existing content. -
Copy and paste the following sample code into the
index.js
file:import custom_greeting from 'ic:canisters/custom_greeting'; import * as React from 'react'; import { render } from 'react-dom'; class MyHello extends React.Component { constructor(props) { super(props); this.state = { name: 'Name', message: '', }; } async doGreet() { const greeting = await custom_greeting.greet(this.state.name); this.setState({ ...this.state, message: greeting }); } onNameChange(ev) { this.setState({ ...this.state, name: ev.target.value }); } render() { return ( <div style={{ "font-size": "30px" }}> <div style={{ "background-color": "yellow" }}> <p>Greetings, from DFINITY!</p> <p> Type your message in the Name input field, then click <b> Get Greeting</b> to display the result.</p> </div> <div style={{ "margin": "30px" }}> <input id="name" value={this.state.name} onChange={ev => this.onNameChange(ev)}></input> <button onClick={() => this.doGreet()}>Get Greeting!</button> </div> <div>Greeting is: "<span style={{ "color": "blue" }}>{this.state.message}</span>"</div> </div> ); } } render(<MyHello />, document.getElementById('app'));
-
Rename the modified
index.js
file asindex.jsx
by running the following command:mv src/custom_greeting_assets/public/index.js src/custom_greeting_assets/public/index.jsx
Start the local network
Before you can build the custom_greeting
project, you need to connect to the Internet Computer network either running locally in your development environment or running remotely on a sub-network that you can access.
To start the network locally:
-
Open a new terminal window or tab on your local computer and navigate to your project directory.
For example, you can do either of the following if running Terminal on macOS:
-
Click Shell, then select New Tab to open a new terminal in your current working directory.
-
Click Shell and select New Window, then run
cd ~/ic-projects/custom_greeting
in the new terminal if yourcustom_greeting
project is in theic-projects
working folder.
You should now have two terminals open with your project directory as your current working directory.
-
-
Start the Internet Computer network on your local computer by running the following command:
dfx start
After you start the local network, the terminal displays messages about network operations.
-
Leave the terminal that displays network operations open and switch your focus to your original terminal where you created your project.
Register canister identifiers
After you connect to the Internet Computer network running locally in your development environment, you can register with the network to generate unique, network-specific canister identifiers for your project.
To register canister identifiers for the local network:
-
Check that you are still in your project directory, if needed.
-
Register unique canister identifiers for the canisters in the project by running the following command:
dfx canister create --all
The command displays the network-specific canister identifiers for the canisters defined in the
dfx.json
configuration file."custom_greeting" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" "custom_greeting_assets" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
Build and deploy the program
You are now ready to compile the program with the new front-end into a WebAssembly module for your project.
To build and deploy the program with the new front-end:
-
Open a terminal shell on your local computer, if you don’t already have one open.
-
Navigate to the root of your
custom_greeting
project folder. -
Build the executable canister by running the following command:
dfx build
The command displays output indicating that the build is successful.
Building canisters... Building frontend...
-
Deploy your
custom_greeting
project on the local network by running the following command:dfx canister install --all
The command output displays output similar to the following:
Installing code for canister custom_greeting, with canister_id 75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q Installing code for canister custom_greeting_assets, with canister_id cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
-
Copy the canister identifier for the
custom_greeting_assets
canister to the clipboard or a notepad application.
View the new front-end
You can now access the new front-end for the default program by entering the canister identifier for the assets canister in a browser.
To view the custom front-end:
-
Open a browser and navigate to the
local
network address and port number specified in thedfx.json
configuration file.For example, if you are using the default binding for the local network, navigate to
127.0.0.1:8000/
.To specify the canister you want the web server to display, add the
canisterId
parameter and thecustom_greeting_assets
canister identifier to the URL using the following syntax:?canisterId=<YOUR-CANISTER-IDENTIFIER>
For example, the full URL should look similar to the following but with the
_canister_identifier_
that was returned for thecustom_greeting_assets
canister when you ran thedfx canister install
command:http://127.0.0.1:8000/?canisterId=cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
-
Verify that you are prompted to type a greeting.
For example:
-
Replace Name in the input field with the text you want to display, then click Get Greeting to see the result.
For example:
Revise the front-end and test your changes
After viewing the front-end, you might want to make some changes.
To modify the front-end:
-
Open the
index.jsx
file in a text editor and modify its style settings. For example, you might want to change the font family and use a placeholder for the input field by making changes similar to the following:import custom_greeting from 'ic:canisters/custom_greeting'; import * as React from 'react'; import { render } from 'react-dom'; class MyHello extends React.Component { constructor(props) { super(props); this.state = { name: '', message: '', }; } async doGreet() { const greeting = await custom_greeting.greet(this.state.name); this.setState({ ...this.state, message: greeting }); } onNameChange(ev) { this.setState({ ...this.state, name: ev.target.value }); } render() { return ( <div style={{ "font-family": "sans-serif" }}> <div style={{ "font-size": "30px" }}> <p>Greetings, from DFINITY!</p> <p> Type your message in the Name input field, then click <b> Get Greeting</b> to display the result.</p> <div style={{ "margin": "30px" }}> <input id="name" placeholder="Type text here" value={this.state.name} onChange={ev => this.onNameChange(ev)}></input> <button onClick={() => this.doGreet()}>Get Greeting!</button> </div> <div>Greeting is: "<span style={{ "color": "green" }}>{this.state.message}</span>"</div> </div> </div> ); } } render(<MyHello />, document.getElementById('app'));
-
Rebuild the project with your changes by running the following command.
dfx build
-
Deploy your project changes by running the following command:
dfx canister install --all --mode reinstall
-
View the results in the browser by reloading the page that displays the
custom_greeting_assets
canister.For example:
-
Type a new message and see your new greeting. For example:
Stop the local network
After you finish experimenting with the front-end for your program, you can stop the local Internet Computer network so that it doesn’t continue running in the background.
To stop the local network:
-
In the terminal that displays network operations, press Control-C to interrupt the local network process.
-
Stop the Internet Computer network by running the following command:
dfx stop