Somnia Docs
Developer DiscordTestnet Homepage
Somnia Documentation
Somnia Documentation
  • 📍Introduction
  • 🔥Get Started
    • Connect your Wallet
    • Request STT Tokens & Try sending tokens to a Random address
    • Removing the Somnia Devnet Network
    • Update the block explorer in metamask
  • Developer
    • Network Info
    • Resources & Important Links
    • Add Custom token in Metamask
    • Tutorials
      • How to Deploy Your First Smart Contract to Somnia Network
      • Create and Deploy your ERC20 Smart Contract to Somnia Network
      • Deploy and Verify A Smart Contract on Somnia using Hardhat
      • Deploy a Smart Contract on Somnia Testnet using Foundry
      • How to Connect to Somnia Network via Viem Library
      • How to Setup MetaMask Authentication to Connect Somnia Network
      • Build a Simple DAO Smart Contract
      • How To Build A User Interface For DAO Smart Contract p1
      • How To Build A User Interface For DAO Smart Contract p2
      • How To Build A User Interface For DAO Smart Contract p3
    • Partners
      • How to deploy Smart Contracts to Somnia using Thirdweb
      • Integrate ConnectKit with Somnia in a Next.js Application
      • Integrating RainbowKit with Somnia in a Next.js Application
      • Integrating DIA Oracles on Somnia
      • Indexing Data on Somnia using Graph Services
      • Somnia Account Abstraction Apps using Thirdweb React SDK
      • Build a NextJS UI for Subgraphs on Somnia
      • Deploy a Subgraph on Somnia using Ormi
    • Infrastructure Providers
      • RPC
      • Oracles
      • Safes
      • Explorers
      • SDKs
  • 📜Litepaper
    • Mission
    • Problem
  • ⛓️Somnia Blockchain
    • Overview
    • MultiStream Consensus
    • Accelerated Sequential Execution
    • Somnia's IceDB
    • Advanced Compression Techniques
    • Security
    • Use Cases
  • 🌐Ecosystem
    • Protocols
      • SOM0
      • SOM1
    • Experiences
      • Metaverse Browser
      • Somnia Playground
    • Content Creation
  • 🌑Conclusion
Powered by GitBook
On this page
  • Pre-requisites:
  • Create Your Next.js Project
  • Setting Up a React Context for Global State
  • Creating a Global NavBar in _app.js
  • NavBar
  • Test Your Setup
  • 5. Next Steps
Export as PDF
  1. Developer
  2. Tutorials

How To Build A User Interface For DAO Smart Contract p1

PreviousBuild a Simple DAO Smart ContractNextHow To Build A User Interface For DAO Smart Contract p2

Last updated 17 days ago

Somnia empowers developers to build applications for mass adoption. Smart Contracts deployed on the Somnia Blockchain will sometimes require building a User Interface. This guide will teach you how to build a user interface for a using Next.js and React Context. It is divided into three parts. At the end of this guide, you’ll learn how to:

  1. Initialize a Next.js project.

  2. Set up a global state using the Context API ( hook).

  3. Add a global NavBar in _app.js so it appears on every page.

You will have a basic skeleton of a DApp, ready for READ/WRITE operations and UI components —topics we’ll cover in the subsequent articles.


Pre-requisites:

  1. This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript.

  2. To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to .

Create Your Next.js Project

To create a NextJS project, run the command:

npx create-next-app my-dapp-ui

Accept the prompts and change directory into the folder after the build is completed.

This gives you a minimal Next.js setup with a pages folder (holding your routes), a public folder (for static assets), and config files.

(Optional) Add Tailwind CSS

If you plan to style your app with Tailwind, install and configure it now:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Then edit your tailwind.config.js:

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Finally, include Tailwind in styles/globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Setting Up a React Context for Global State

  • Create a folder and name it contexts at the project root or inside pages directory.

  • Inside it, create a file called walletcontext.js add the following code to the file:

walletcontext.js
import { createContext, useContext, useState } from "react";

const WalletContext = createContext();

export function WalletProvider({ children }) {
  const [connected, setConnected] = useState(false);
  const [address, setAddress] = useState("");
  
  async function connectToMetaMask() {
    if (typeof window !== "undefined" && window.ethereum) {
      try {
        await window.ethereum.request({ method: "eth_requestAccounts" });
        // For simplicity, get the first address
        const [userAddress] = window.ethereum.selectedAddress
          ? [window.ethereum.selectedAddress]
          : [];
        setAddress(userAddress);
         setConnected(true);
      } catch (err) {
        console.error("User denied account access:", err);
      }
    } else {
      console.log("MetaMask is not installed!");
    }
  }

  function disconnectWallet() {
    setConnected(false);
    setAddress("");
  }

  // Return the context provider
  return (
<WalletContext.Provider
      value={{
        connected,
        address,
        connectToMetaMask,
        disconnectWallet,
      }}
    >
      {children}
    </WalletContext.Provider>
  );
}


export function useWallet() {
  return useContext(WalletContext);
}

We parse three class methods from React: createContext, useContext, anduseState

useWallet() is a custom hook, so any page or component can do:

const { connected, ... } = useWallet()

to access the global wallet state i.e. any of the Wallet.Provider methods

connectToMetaMask() triggers the MetaMask connection flow.

WalletProvider manages State and Methods in the application.


Creating a Global NavBar in _app.js

Next.js automatically uses pages/_app.js to initialize every page. We will wrap the entire app in the WalletProvider inside _app.js and inject a NavBar menu that appears site-wide in the application

Create the _app.js and add the code:

import "../styles/globals.css";
import { WalletProvider } from "../contexts/walletcontext";
import NavBar from "../components/navbar";

function MyApp({ Component, pageProps }) {
  return (
    <WalletProvider>
      <NavBar />
      <main className="pt-16">
        <Component {...pageProps} />
      </main>
    </WalletProvider>
  );
}

export default MyApp;

<WalletProvider> wraps the entire <Component /> tree so that every page can share the same wallet state.

<NavBar /> is placed above <main>, so it’s visible on all pages. We give <main> a pt-16 to avoid content hiding behind a fixed navbar.

NavBar

Create a sub directory components and add a file called navbar.js Add the code:

import { useWallet } from "../contexts/walletcontext";
import Link from "next/link";

export default function NavBar() {
  const { connected, address, disconnectWallet } = useWallet();


  return (
    <nav className="fixed w-full bg-white shadow z-50">
      <div className="mx-auto max-w-7xl px-4 flex h-16 items-center justify-between">
        <Link href="/">
          <h1 className="text-xl font-bold text-blue-600">MyDAO</h1>
        </Link>
<div>
          {connected ? (
            <div className="flex items-center space-x-4 text-blue-500">
              <span>{address.slice(0, 6)}...{address.slice(-4)}</span>
              <button onClick={disconnectWallet} className="px-4 py-2 bg-red-500 text-white rounded">
                Logout
              </button>
            </div>
          ) : (
            <span className="text-gray-500">Not connected</span>
          )}
        </div>
      </div>
    </nav>
  );
}

It uses useWallet() to read the global connected and addressstates, and the disconnectWallet function. The truncated address is displayed if a user is logged into the App or “Not connected” otherwise. A Logout button calls disconnectWallet() to reset the global state.


Test Your Setup

Start the dev server:

npm run dev

Open http://localhost:3000 in a Web Browser. You should see your NavBar at the top.

Because we haven’t built any advanced pages yet, you will see a blank home page. The important part is that your WalletContext and global NavBar are in place and ready for the next steps.


5. Next Steps

  • Article 2 shows you how to implement READ/WRITE operations (e.g., deposit, create proposals, vote, etc.) across different Next.js pages—using the same WalletContext to handle contract calls.

  • Article 3 will focus on UI components, like forms, buttons, and event handling, tying it all together into a polished user interface.

Congratulations! You have a clean foundation, a Next.js project configured with Tailwind, a global context to manage wallet states, and a NavBar appearing across all routes. This sets the stage for adding contract interactions and advanced UI flows in the subsequent articles. Happy building!

In many DApps, including this one, developers will manage Wallet connection and State globally so each page and component in the project can access it without repetitive code. This can be achieved by a following React patterns.

createContext is used to create a that components can provide or read. In this example, we assign createContext to the WalletContext variable and call the Provider method on each State and Function to make them available throughout the application.

DAO Smart Contract
useContext
Connect Your Wallet
Context
context
Not Logged In
Logged In