5. Build a Hello World Frontend
In the previous examples, we invoked the contracts using the Stellar CLI, and in this last part of the guide we'll create a web app that interacts with the Hello World contract through TypeScript bindings.
This example shows one way of creating a binding between a contract and a frontend, for a more comprehensive guide see the Build a Dapp Frontend documentation.
Initialize a frontend toolchain
You can build a Stellar dapp with any frontend toolchain or integrate it into any existing full-stack app. For this tutorial, we're going to use Astro. Astro works with React, Vue, Svelte, any other UI library, or no UI library at all. In this tutorial, we're not using a UI library. The smart contract-specific parts of this tutorial will be similar no matter what frontend toolchain you use.
If you're new to frontend, don't worry. We won't go too deep. But it will be useful for you to see and experience the frontend development process used by smart contract apps. We'll cover the relevant bits of JavaScript and Astro, but teaching all of frontend development and Astro is beyond the scope of this tutorial.
Let's get started.
You're going to need Node.js v20 or greater. If you haven't yet, install it now.
We want to create an Astro project with the Hello World contract from the previous lessons integrated. To do this, we install the default Astro project:
npm create astro@latest
This project has the following directory structure.
extra-escape
├── astro.config.mjs
├── package-lock.json
├── package.json
├── packages
├── public
├── README.md
├── src
│ ├── assets
│ │ ├── astro.svg
│ │ └── background.svg
│ ├── components
│ │ └── Welcome.astro
│ ├── layouts
│ │ └── Layout.astro
│ └── pages
│ └── index.astro
└── tsconfig.json
Since we already deployed these contracts with aliases, we can reuse the generated contract ID files by copying them from the soroban-hello-world/.stellar
directory into this project:
cp -R ../.stellar/ .stellar
Generate an NPM package for the Hello World contract
Before we open the new frontend files, let's generate an NPM package for the Hello World contract. This is our suggested way to interact with contracts from frontends. These generated libraries work with any JavaScript project (not a specific UI like React), and make it easy to work with some of the trickiest bits of smart contracts on Stellar, like encoding XDR.
This is going to use the CLI command stellar contract bindings typescript
:
stellar contract bindings typescript \
--network testnet \
--contract-id hello_world \
--output-dir packages/hello_world
Notice that we were able to use the contract alias, hello_world
, in place of the contract id!
The binding will be created in as a NPM package in the directory packages/hello_world
as specified in the CLI command. We'll need to build the bindings package, since (in its initial state) the package is mostly TypeScript types and stubs for the various contract functions.
cd packages/hello_world
npm install
npm run build
cd ../..
We attempt to keep the code in these generated libraries readable, so go ahead and look around. Open up the new packages/hello_world
directory in your editor. If you've built or contributed to Node projects, it will all look familiar. You'll see a package.json
file, a src
directory, a tsconfig.json
, and even a README.
Call the contract from the frontend
Now let's open up src/pages/index.astro
and use the binding to call the hello
contract function with an argument.
The default Astro project consists of a page (pages/index.astro
) and a welcome component (component/Welcome.astro
), and we don't need any of that code. Replace the pages/index.astro
code with this code (the welcome component will not be needed):
---
import * as Client from './packages/hello_world';
const contract = new Client.Client({
...Client.networks.testnet,
rpcUrl: 'https://soroban-testnet.stellar.org:443'
});
const { result } = await contract.hello({to: "Devs!"});
const greeting = result.join(" ");
---
<h1>{greeting}</h1>
First we import the binding library, and then we need to define a contract client we can use for invoking the contract function we deployed to testnet in a previous step.
The hello()
contract function is invoked synchronously with the argument {to: "Devs!"}
and the expected response is an array consisting of "Hello" and "Devs!". We join the result array and the constant greeting
should now hold the text Hello Devs!
Jumping down to the HTML section we now want to display the greeting
text in the browser. Let's see it in action! Start the dev server:
npm run dev
And open localhost:4321 in your browser. You should see the greeting from the contract!
You can try updating the argument to { to: 'Stellar' }
. When you save the file, the page will automatically update.
When you start up the dev server with npm run dev
, you will see similar output in your terminal as when you ran npm run init
. This is because the dev
script in package.json is set up to run npm run init
and astro dev
, so that you can ensure that your deployed contract and your generated NPM package are always in sync. If you want to just start the dev server without the initialize.js script, you can run npm run astro dev
.