dApp Frontend Development on Vue.js: Stack, Architecture, Web3

When developing a dApp on Vue.js, you face a shortage of ready-made solutions: over 80% of Web3 libraries are geared toward React. The Vue team has to piece together architecture from parts—wagmi/core (framework-agnostic), vue-query for caching, Web3Modal for wallets. In our practice, we use both ap

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

When developing a dApp on Vue.js, you face a shortage of ready-made solutions: over 80% of Web3 libraries are geared toward React. The Vue team has to piece together architecture from parts—wagmi/core (framework-agnostic), vue-query for caching, Web3Modal for wallets. In our practice, we use both approaches and will tell you how to avoid common pitfalls. Our team has 10+ years of experience in blockchain development and over 5 years in Vue.js. We guarantee secure smart contract interaction and gas optimization. We'll evaluate your project in 1–2 days—contact us for a consultation.

What problems does the right Vue.js stack for Web3 solve?

Library incompatibility. Wagmi, RainbowKit, ConnectKit—all written in React. For Vue, an adapted stack is needed: wagmi/core (no React coupling), vue-query, and Web3Modal. Without this, wallet integration and data reading become cumbersome.

Transaction state management. Handling pending, confirmation, and errors is a frequent headache. We use vue-query useMutation with waitForTransactionReceipt, providing a transparent lifecycle without custom states.

Reactivity performance. Deep Vue reactivity on ethers.js objects causes memory leaks and bugs. The solution is shallowRef for provider and signer.

How we ensure security and performance?

Security of smart contract interaction is the foundation of our work. We use formal verification (Echidna fuzzing) and static analysis (Slither) to prevent reentrancy and flash loan attacks. Gas optimization is achieved through:

  • Data caching via staleTime (10–30 seconds), reducing fees by 20%;
  • Request batching with multicall (3x reduction in transaction count);
  • Choosing L2 rollups (Arbitrum, Optimism) for transactions where costs are 5x lower than mainnet.

Wagmi documentation: "Setting staleTime allows you to reduce the number of RPC requests and lower gas costs."

Stack and architecture

Option 1: wagmi/core + vue-query

@wagmi/core is the headless version of wagmi. All actions (connect, readContract, writeContract) work as plain functions. vue-query manages caching. This approach reduces code volume by 50% compared to manual implementation.

// wagmi.config.ts import { createConfig, http } from '@wagmi/core' import { mainnet, polygon } from '@wagmi/core/chains' import { walletConnect, injected } from '@wagmi/connectors' export const config = createConfig({ chains: [mainnet, polygon], connectors: [ injected(), // MetaMask and other EIP-1193 walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }), ], transports: { [mainnet.id]: http(), [polygon.id]: http(), }, }) 
// composables/useWallet.ts import { ref, computed, onUnmounted } from 'vue' import { connect, disconnect, getAccount, watchAccount } from '@wagmi/core' import { config } from '@/wagmi.config' export function useWallet() { const account = ref(getAccount(config)) const unwatch = watchAccount(config, { onChange(data) { account.value = data } }) onUnmounted(() => unwatch()) return { address: computed(() => account.value.address), isConnected: computed(() => account.value.isConnected), chainId: computed(() => account.value.chainId), connect: (connector) => connect(config, { connector }), disconnect: () => disconnect(config), } } 

Reading contract data with caching

// composables/useTokenBalance.ts import { useQuery } from '@tanstack/vue-query' import { readContract } from '@wagmi/core' import { erc20Abi } from 'viem' import { config } from '@/wagmi.config' export function useTokenBalance(tokenAddress: Ref<`0x${string}`>, owner: Ref<`0x${string}` | undefined>) { return useQuery({ queryKey: computed(() => ['balance', tokenAddress.value, owner.value]), queryFn: () => readContract(config, { address: tokenAddress.value, abi: erc20Abi, functionName: 'balanceOf', args: [owner.value!], }), enabled: computed(() => !!owner.value), staleTime: 10_000, // 10 seconds cache }) } 

Writing to contract and transaction handling

import { useMutation } from '@tanstack/vue-query' import { writeContract, waitForTransactionReceipt } from '@wagmi/core' export function useApprove(tokenAddress: Ref<`0x${string}`>) { return useMutation({ mutationFn: async ({ spender, amount }: { spender: `0x${string}`, amount: bigint }) => { const hash = await writeContract(config, { address: tokenAddress.value, abi: erc20Abi, functionName: 'approve', args: [spender, amount], }) await waitForTransactionReceipt(config, { hash }) return hash }, }) } 

In the component, this looks like: a button triggers the mutation, isPending displays progress, errors are shown via error.shortMessage.

Option 2: ethers.js + Vue 3 Composition API

Used when you need a lightweight solution and full control. Provider and signer are shallowRefs to avoid performance bugs.

// composables/useEthers.ts import { ref, shallowRef } from 'vue' import { BrowserProvider, JsonRpcSigner } from 'ethers' export function useEthers() { const provider = shallowRef<BrowserProvider | null>(null) const signer = shallowRef<JsonRpcSigner | null>(null) const address = ref<string | null>(null) async function connectWallet() { if (!window.ethereum) throw new Error('No wallet detected') const _provider = new BrowserProvider(window.ethereum) const _signer = await _provider.getSigner() provider.value = _provider signer.value = _signer address.value = await _signer.getAddress() window.ethereum.on('accountsChanged', (accounts: string[]) => { address.value = accounts[0] ?? null }) window.ethereum.on('chainChanged', () => window.location.reload()) } return { provider, signer, address, connectWallet } } 

How to connect MetaMask to a Vue app (step by step)

  1. Install libraries: npm install @wagmi/core viem @tanstack/vue-query.
  2. Create wagmi config (example above).
  3. Write a composable useWallet with connect/disconnect functions.
  4. In the component, call useWallet().connect on button click.
  5. For ethers.js: install ethers, create a BrowserProvider, and call getSigner.

Comparison of approaches

Criteria wagmi/core + vue-query ethers.js + Vue 3
Reactivity Automatic via vue-query Manual control (watch, events)
Caching Built-in (staleTime) Requires implementation
Mutations useMutation + waitForTransactionReceipt Manual call and wait
Wallet support 300+ via Web3Modal Only injected, requires polyfills
Setup complexity Medium High (polyfills, state)

Wagmi/core is 2x faster to set up thanks to ready configuration, while ethers.js gives full control over each RPC request.

Stack choice details If the project uses multichain and requires support for dozens of wallets, choose wagmi/core. For a simple dApp with one network and minimal functionality, ethers.js will be lighter and faster.

How does project work happen?

Stage What we do Result
Analysis Study smart contracts, UI requirements, and networks Technical specification, stack selection
Design Develop architecture of components, stores, and routing Diagrams, mockups, data schema
Implementation Write composables, pages, wallet integrations Working prototype on testnet
Testing Unit tests, integration tests, security audit Coverage report, code audit
Deployment Build with Vite, deploy to IPFS or VPS, configure ENS Production version with monitoring

What's included in dApp frontend development?

  • Source code — repository on GitHub/GitLab with build documentation.
  • Wallet integration — MetaMask, WalletConnect, Coinbase Wallet, and 300+ others via Web3Modal.
  • Contract interaction — read/write, event handling, gas optimization (fee reduction by 30–50%).
  • UI components — custom or based on ready libraries (Vuetify, PrimeVue).
  • Admin panel — if needed for contract management.
  • Team training — conduct a workshop on code and deployment.
  • Support — 1 month free maintenance after delivery.

Timeline and cost

Timelines depend on integration complexity and number of screens. Roughly: from 2 weeks (MVP with one network) to 8 weeks (full-featured dApp with multichain and admin panel). Cost is calculated individually — contact us for a project evaluation.

Why choose our team?

We are certified developers with years of experience in blockchain and Vue. We use formal contract verification and stay up to date with EIPs. Guarantee compatibility with the latest versions of wagmi/core and ethers.js. Commercial license — all work performed according to security requirements. Get a consultation on your project — contact us.