Back to Blog
·11 min

DAO Governance Platform: Build Decentralized Autonomous Organizations

Decentralized Autonomous Organizations (DAOs) represent a new paradigm for collective decision-making. In 2026, DAOs manage billions in treasury assets, coordinate thousands of contributors, and govern everything from DeFi protocols to physical communities. Building a DAO governance platform requires robust voting mechanisms, treasury management, and member onboarding flows.

Core DAO Components

A complete DAO governance platform built with the DAO Governance App template includes:

  • - Proposal Creation: Submit on-chain and off-chain proposals with parameters
  • - Voting Mechanisms: Token-weighted, quadratic, and NFT-based voting
  • - Treasury Management: Multi-sig treasury with spending proposals
  • - Member Onboarding: Token-gated membership with delegation
  • - Governance Analytics: Participation rates, voting power distribution, and proposal history

Setting Up the DAO Template

      git clone https://github.com/breafio/dao-governance-app.git my-dao
      cd my-dao
      pnpm install
      cp .env.example .env.local

Smart Contract Architecture

The governance system uses battle-tested OpenZeppelin contracts:

      // contracts/Governor.sol
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.20;

      import "@openzeppelin/contracts/governance/Governor.sol";
      import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
      import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
      import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
      import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";

      contract DAOGovernor is
          Governor,
          GovernorSettings,
          GovernorCountingSimple,
          GovernorVotesQuorumFraction,
          GovernorTimelockControl
      {
          constructor(
              IVotes _token,
              TimelockController _timelock,
              uint256 _votingDelay,
              uint256 _votingPeriod,
              uint256 _quorumPercentage
          )
              Governor("DAO Governor")
              GovernorSettings(_votingDelay, _votingPeriod, 0)
              GovernorVotesQuorumFraction(_quorumPercentage)
              GovernorTimelockControl(_timelock)
          {}

          function votingDelay() public view override(Governor, GovernorSettings) returns (uint256) {
              return super.votingDelay();
          }

          function votingPeriod() public view override(Governor, GovernorSettings) returns (uint256) {
              return super.votingPeriod();
          }

          function quorum(uint256 blockNumber)
              public view override(Governor, GovernorVotesQuorumFraction) returns (uint256)
          {
              return super.quorum(blockNumber);
          }

          function _execute(
              uint256 proposalId,
              address[] memory targets,
              uint256[] memory values,
              bytes[] memory calldatas,
              bytes32 descriptionHash
          ) internal override(Governor, GovernorTimelockControl) {
              super._execute(proposalId, targets, values, calldatas, descriptionHash);
          }
      }

Proposal Creation Flow

The frontend allows members to create proposals with a clean interface:

      'use client'

      import { useState } from 'react'
      import { useWriteContract } from 'wagmi'
      import { parseEther } from 'viem'

      interface ProposalAction {
        target: string
        value: string
        signature: string
        calldata: string
        description: string
      }

      export function CreateProposal() {
        const [title, setTitle] = useState('')
        const [description, setDescription] = useState('')
        const [actions, setActions] = useState<ProposalAction[]>([{
          target: '', value: '0', signature: '', calldata: '', description: ''
        }])
        const { writeContract, isPending } = useWriteContract()

        const addAction = () => {
          setActions([...actions, {
            target: '', value: '0', signature: '', calldata: '', description: ''
          }])
        }

        const submitProposal = async () => {
          const fullDescription = `# ${title}

${description}`
          const targets = actions.map(a => a.target as string)
          const values = actions.map(a => parseEther(a.value || '0'))
          const calldatas = actions.map(a => a.calldata as string)

          writeContract({
            address: process.env.NEXT_PUBLIC_GOVERNOR_ADDRESS as string,
            abi: governorAbi,
            functionName: 'propose',
            args: [targets, values, calldatas, fullDescription],
          })
        }

        return (
          <div className="mx-auto max-w-2xl">
            <h2 className="text-xl font-bold text-gray-900">Create Proposal</h2>

            <div className="mt-6 space-y-4">
              <div>
                <label className="block text-sm font-medium text-gray-700">Title</label>
                <input
                  value={title}
                  onChange={e => setTitle(e.target.value)}
                  className="mt-1 w-full rounded-xl border px-4 py-2"
                  placeholder="Proposal title"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-gray-700">Description</label>
                <textarea
                  value={description}
                  onChange={e => setDescription(e.target.value)}
                  rows={5}
                  className="mt-1 w-full rounded-xl border px-4 py-2"
                  placeholder="Describe your proposal"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-gray-700">Actions</label>
                {actions.map((action, idx) => (
                  <div key={idx} className="mt-2 rounded-lg border p-3">
                    <input
                      value={action.target}
                      onChange={e => {
                        const newActions = [...actions]
                        newActions[idx] = { ...action, target: e.target.value }
                        setActions(newActions)
                      }}
                      className="w-full rounded-lg border px-3 py-1.5 text-sm"
                      placeholder="Target contract address"
                    />
                  </div>
                ))}
                <button onClick={addAction} className="mt-2 text-sm text-brand-600 hover:text-brand-700">
                  + Add Action
                </button>
              </div>

              <button
                onClick={submitProposal}
                disabled={isPending}
                className="w-full rounded-xl bg-brand-600 py-3 font-semibold text-white disabled:opacity-50"
              >
                {isPending ? 'Submitting...' : 'Submit Proposal'}
              </button>
            </div>
          </div>
        )
      }

Voting Interface

Members can vote with their token weight or delegate voting power:

      'use client'

      import { useState } from 'react'
      import { useWriteContract, useReadContract } from 'wagmi'

      type VoteType = 0 | 1 | 2 // Against, For, Abstain

      export function VoteOnProposal({ proposalId }: { proposalId: bigint }) {
        const [selectedVote, setSelectedVote] = useState<VoteType | null>(null)
        const { writeContract, isPending } = useWriteContract()

        const { data: proposal } = useReadContract({
          address: process.env.NEXT_PUBLIC_GOVERNOR_ADDRESS as string,
          abi: governorAbi,
          functionName: 'proposalVotes',
          args: [proposalId],
        })

        const castVote = () => {
          if (selectedVote === null) return
          writeContract({
            address: process.env.NEXT_PUBLIC_GOVERNOR_ADDRESS as string,
            abi: governorAbi,
            functionName: 'castVote',
            args: [proposalId, selectedVote],
          })
        }

        return (
          <div className="rounded-xl border p-6">
            <h3 className="font-semibold text-gray-900">Cast Your Vote</h3>
            <div className="mt-4 grid grid-cols-3 gap-3">
              {[
                { type: 2 as VoteType, label: 'For', color: 'bg-green-600' },
                { type: 0 as VoteType, label: 'Against', color: 'bg-red-600' },
                { type: 1 as VoteType, label: 'Abstain', color: 'bg-gray-500' },
              ].map(option => (
                <button
                  key={option.label}
                  onClick={() => setSelectedVote(option.type)}
                  className={`rounded-lg py-2 text-sm font-medium text-white transition-all ${
                    selectedVote === option.type
                      ? option.color + ' ring-2 ring-offset-2'
                      : 'bg-gray-200 text-gray-700 hover:bg-gray-300'
                  }`}
                >
                  {option.label}
                </button>
              ))}
            </div>
            <button
              onClick={castVote}
              disabled={selectedVote === null || isPending}
              className="mt-4 w-full rounded-xl bg-brand-600 py-2 font-semibold text-white disabled:opacity-50"
            >
              {isPending ? 'Voting...' : 'Submit Vote'}
            </button>
          </div>
        )
      }

The Bottom Line

DAO governance platforms require careful smart contract architecture, intuitive proposal workflows, and transparent voting mechanisms. The DAO Governance App template provides a complete foundation with OpenZeppelin-based contracts, token-weighted voting, treasury management, and member onboarding.

Ready to Build?

Get started with our production-ready starter kits and ship your project faster.

Browse Starter Kits