Розробка фронтенда dApp на Nuxt.js
Vue-екосистема у Web3 програє React за кількістю готових бібліотек, але не за можливостями. Основна складність — більшість Web3 tooling написано React-first: wagmi, ConnectKit, RainbowKit працюють тільки з React. Для Nuxt потрібен або інший стек, або обёртки. Розберемо, що реально працює в 2024-2025.
Web3-стек для Nuxt 3
Підключення гаманців
@wagmi/core + @web3modal/wagmi — wagmi core працює без React, Vue-обёртка @wagmi/vue з'явилась у wagmi v2. Web3Modal підтримує Vue через окремий пакет @web3modal/wagmi/vue. Це найбільш зрілий варіант:
// 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,
})
})
Плагін позначений .client.ts — важливо, тому що Web3 бібліотеки несумісні з SSR: звертаються до window, localStorage, ethereum інжектору.
viem для читання даних з блокчейну — працює в будь-якому окруженні:
// 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-специфіка
Nuxt 3 за замовчуванням використовує SSR, і це створює тертя з Web3:
Проблема hydration — window.ethereum існує тільки в браузері. Будь-який компонент, який читає стан гаманця, повинен бути або в <ClientOnly>, або перевіряти process.client:
<template>
<ClientOnly>
<WalletButton />
<template #fallback>
<button disabled>Загрузка...</button>
</template>
</ClientOnly>
</template>
Nuxt plugin з ssr: false — альтернатива <ClientOnly>:
// nuxt.config.ts
export default defineNuxtConfig({
plugins: [
{ src: '~/plugins/wagmi.client.ts', mode: 'client' }
]
})
useNuxtApp().$wagmi — після інічіалізації в плагіні wagmi config доступний через Nuxt's provide/inject паттерн.
Composables для Web3
Різниця між React hooks та Vue composables у контексті Web3: Vue реактивність через ref/computed краще працює для derived state, який змінюється при зміні аккаунту або мережі.
// 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 }
}
Трансакції та стан
Управління статусом трансакції — типовий pain point. wagmi v2 надає useWriteContract + useWaitForTransactionReceipt:
const { writeContract, data: hash } = useWriteContract()
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
Паттерн для UI: показуємо три стани — "підпишіть у гаманці", "трансакція відправлена (hash)", "підтверджена". Nuxt + Pinia зручно тримають цей стан глобально для toast-сповіщень.
Типовий стек проекту
| Задача | Бібліотека |
|---|---|
| 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 |
Строки розробки фронтенда dApp на Nuxt — 1-2 тижні в залежності від складності: базове підключення гаманця та читання контрактів займає 3-4 дні, повноцінний dApp з кількома екранами, трансакціями та обробкою помилок — 1-1.5 тижні.







