Add front-end assets
The tutorials introduced a few basic approaches to building a front-end user interface for your projects. For example, the tutorials demonstrated:
-
Using Candid as a bare-bones interface to expose and test the functions in a canister.
-
Using raw HTML and JavaScript to display a simple HTML entry page.
-
Using React and compiled JavaScript to embed HTML attributes and elements directly in a page.
-
Using React and TypeScript to import CSS properties from an external file.
This section takes a closer look at the default front-end template, front-end configuration options, and using other frameworks to build the user interface for your projects.
How the default templates are used
As you might have noticed in the tutorials, projects include template index.js and webpack.config.js files.
By default, the index.js file explicitly instantiates an instance of the HTTP agent. This agent provides an interface for interactions between the user-facing front-end and the Internet Computer.
After creating the agent, the index.js file constructs an actor for the canisters defined in your project’s dfx.json file.
If you review the code in the index.js file, you see that it performs these steps using the following code block:
import { Actor, HttpAgent } from '@dfinity/agent';
import { idlFactory as hello_idl, canisterId as hello_id } from 'dfx-generated/hello';
const agent = new HttpAgent();
const hello = Actor.createActor(hello_idl, { agent, canisterId: hello_id });
After the agent and the actor are created for the project, the index.js file provides a placeholder for the JavaScript you use to interact with the document object model (DOM) and HTML for your application.
Modifying the webpack configuration
Because webpack is a popular and highly-configurable module bundler for JavaScript-based applications, new projects create a default webpack.config.js file that makes it easy to add the specific modules—such as react and markdown—that you want to use.
If you review the code in the template webpack.config.js file, you see that it constructs aliases for the canisters defined in your project’s dfx.json file. The aliases can then be used to reference the canisters when imported as modules by the front-end.
You can see these steps in the following code block:
// List of all aliases for canisters. This creates the module alias for
// the `import ... from "dfx-generated/canisters/xyz"` where xyz is the name of a
// canister.
const aliases = Object.entries(dfxJson.canisters).reduce(
(acc, [name, _value]) => {
// Get the network name, or `local` by default.
const networkName = process.env["DFX_NETWORK"] || "local";
const outputRoot = path.join(
__dirname,
".dfx",
networkName,
"canisters",
name
);
return {
...acc,
["dfx-generated/" + name]: path.join(outputRoot, name + ".js"),
};
},
{}
);
Entry and output configuration
After creating canister aliases, the template webpack.config.js file generates a webpack configuration for the front-end and adds the resulting files to the output directory for your project directory.
In many cases, you can use the default webpack.config.js file as-is, without any modification, or you can add plug-ins, modules, and other custom configuration to suit your needs.
The specific changes you make to the webpack.config.js configuration largely depend on the other tools and frameworks you want to use.
For example, if you have experimented with the Customize the front-end or Add a stylesheet front-end tutorials, you might have modified the following section to work with React JavaScript:
module: {
rules: [
{ test: /\.(ts|tsx|jsx)$/, loader: "ts-loader" },
{ test: /\.css$/, use: ['style-loader','css-loader'] }
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, info.frontend.entrypoint),
filename: 'index.html',
chunks: ['index'],
})
],
};
}
Ensuring node is available in a project
Because projects rely on webpack to provide the framework for the default front-end, you must have node.js installed in your development environment and accessible in the project directory.
-
If you want to develop your project without using the default webpack configuration and canister aliases, you can remove the
assetscanister from thedfx.jsonfile or build your project using a specific canister name. For example, you can choose to build only the hello program without front-end assets by running the following command:dfx build hello
-
If you are using the default webpack configuration and running
dfx buildfails, you should try runningnpm installin the project directory then re-runningdfx build. -
If running
npm installin the project directory doesn’t fix the issue, you should check the configuration of thewebpack.config.jsfile for syntax errors.
Using other modules with the React framework
Several tutorials and sample projects in the examples repository illustrate how to add React modules using the npm install command.
You can use these modules to construct the user interface components you want to use in your project.
For example, you might run the following command to install the react-router module:
npm install --save react react-router-dom
You could then use the module to construct a navigation component similar to the following:
import React from 'react';
import { NavLink } from 'react-router-dom';
const Navigation = () => {
return (
<nav className="main-nav">
<ul>
<li><NavLink to="/myphotos">Remember</NavLink></li>
<li><NavLink to="/myvids">Watch</NavLink></li>
<li><NavLink to="/audio">Listen</NavLink></li>
<li><NavLink to="/articles">Read</NavLink></li>
<li><NavLink to="/contribute">Write</NavLink></li>
</ul>
</nav>
);
}
export default Navigation;
Iterate faster using webpack-dev-server
There are a few simple shortcuts you can use to iterate faster in your development environment. For example, you can choose to build and deploy only specific canisters instead of all canisters in a project.
If most of your changes are in the front-end for your application, one of the most effective ways you can iterate faster is by installing and configuring the webpack development server.
The webpack development server—webpack-dev-server—provides in-memory access to the webpack assets, enabling you to make changes and see them reflected in the browser right away using live reloading.
To install and configure webpack-dev-server:
-
Create a new project and change to your project directory.
-
Install the
webpack-dev-serverin the project directory by running the following command:npm install webpack-dev-server -
Open the
webpack.config.jsfile for your project in a text editor. -
Add your network host name and port information after the
outputsection of thewebpack.config.jsfile.For example, if you are using the default host and port information for local development, you would add the following to the
webpack.config.jsfile:devServer: { proxy: { "/api": "http://localhost:8000", }, }, -
Save your changes and close the
webpack.config.jsfile to continue. -
Open the
package.jsonfile for your project in a text editor. -
Add a comma after
"build": "webpack"in thescriptssection. -
Add a new line with
"start": "webpack serve"in thescriptssection.For example:
"start": "webpack serve" -
Save your changes and close the
package.jsonfile to continue. -
Start the Internet Computer locally, if necessary, and deploy as you normally would, for example, by running the
dfx deploycommand. -
Start the webpack development server by running the following command:
npm start -
Open a web browser and navigate to the asset canister for your application using port 8080.
For example:
http://localhost:8080/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai
-
Open a new terminal window or tab and navigate to your project directory.
-
Open the
index.jsfile for your project in a text editor and make changes to the content.For example, you might add an element to the page using JavaScript:
document.body.onload = addElement;
document.body.onload = addElement; function addElement () { // create a new div element const newDiv = document.createElement("div"); // and give it some content const newContent = document.createTextNode("Test live page reloading!"); // add the text node to the newly created div newDiv.appendChild(newContent); // add the newly created element and its content into the DOM const currentDiv = document.getElementById("div1"); document.body.insertBefore(newDiv, currentDiv); } -
Save your changes to the
index.jsfile but leave the editor open to continue making changes. -
Refresh the browser or wait for it to refresh on its own to see your change.
When you are done working on the front-end for your project, you can stop the webpack development server by pressing Control-C.