dApp Frontend Development with Nuxt.js

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
dApp Frontend Development with Nuxt.js
Medium
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

dApp Frontend Development with Nuxt.js

The Vue ecosystem in Web3 loses to React in the number of ready-made libraries, but not in capabilities. The main challenge — most Web3 tooling is written React-first: wagmi, ConnectKit, RainbowKit only work with React. For Nuxt, you need either a different stack or wrappers. Let's look at what actually works in 2024-2025.

Web3 Stack for Nuxt 3

Wallet Connection

@wagmi/core + @web3modal/wagmi — wagmi core works without React, Vue wrapper @wagmi/vue appeared in wagmi v2. Web3Modal supports Vue through a separate package @web3modal/wagmi/vue. This is the most mature option:

// plugins/wagmi.client.ts
import { createWeb3Modal } from '@web3modal/wagmi/vue'
import { createConfig, http } from '@wagmi/vue'
import { mainnet, arbitrum } from '@wagmi/vue/chains'
import { walletConnect, injected } from '@wagmi/vue/connectors'

export default defineNuxtPlugin(() => {
  const config = createConfig({
    chains: [mainnet, arbitrum],
    connectors: [
      walletConnect({ projectId: useRuntimeConfig().public.wcProjectId }),
      injected(),
    ],
    transports: {
      [mainnet.id]: http(),
      [arbitrum.id]: http(),
    },
  })

  createWeb3Modal({
    wagmiConfig: config,
    projectId: useRuntimeConfig().public.wcProjectId,
  })
})

Plugin marked .client.ts is important — Web3 libraries are incompatible with SSR: they access window, localStorage, ethereum injector.

viem for reading blockchain data — works in any environment:

// composables/useContract.ts
import { createPublicClient, http, parseAbi } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

export const useTokenBalance = (address: Ref<`0x${string}` | undefined>) => {
  return useAsyncData(
    `balance-${address.value}`,
    () => address.value
      ? client.readContract({
          address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
          abi: parseAbi(['function balanceOf(address) view returns (uint256)']),
          functionName: 'balanceOf',
          args: [address.value],
        })
      : Promise.resolve(0n),
    { watch: [address] }
  )
}

SSR Specifics

Nuxt 3 uses SSR by default, and this creates friction with Web3:

Hydration problemwindow.ethereum exists only in the browser. Any component reading wallet state must be either in <ClientOnly> or check process.client:

<template>
  <ClientOnly>
    <WalletButton />
    <template #fallback>
      <button disabled>Loading...</button>
    </template>
  </ClientOnly>
</template>

Nuxt plugin with ssr: false — alternative to <ClientOnly>:

// nuxt.config.ts
export default defineNuxtConfig({
  plugins: [
    { src: '~/plugins/wagmi.client.ts', mode: 'client' }
  ]
})

useNuxtApp().$wagmi — after initialization in plugin, wagmi config is available via Nuxt's provide/inject pattern.

Composables for Web3

The difference between React hooks and Vue composables in Web3 context: Vue reactivity through ref/computed works better for derived state that changes when account or network changes.

// composables/useWalletState.ts
export const useWalletState = () => {
  const { address, isConnected, chain } = useAccount()
  
  const shortAddress = computed(() =>
    address.value
      ? `${address.value.slice(0, 6)}...${address.value.slice(-4)}`
      : null
  )
  
  const isWrongNetwork = computed(() =>
    isConnected.value && chain.value?.id !== mainnet.id
  )
  
  return { address, isConnected, shortAddress, isWrongNetwork }
}

Transactions and State

Managing transaction status is a typical pain point. wagmi v2 provides useWriteContract + useWaitForTransactionReceipt:

const { writeContract, data: hash } = useWriteContract()
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })

Pattern for UI: show three states — "sign in wallet", "transaction sent (hash)", "confirmed". Nuxt + Pinia conveniently keep this state globally for toast notifications.

Typical Project Stack

Task Library
Framework Nuxt 3 (Vue 3 + Vite)
Wallet connect @web3modal/wagmi + @wagmi/vue
On-chain reads viem PublicClient
State Pinia
Styling Tailwind CSS 3 / UnoCSS
Data fetching useAsyncData + TanStack Query

Timeline for dApp frontend development on Nuxt — 1-2 weeks depending on complexity: basic wallet connection and contract reads take 3-4 days, full-featured dApp with multiple screens, transactions and error handling — 1.5 weeks.