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)
- Install libraries:
npm install @wagmi/core viem @tanstack/vue-query. - Create wagmi config (example above).
- Write a composable
useWalletwith connect/disconnect functions. - In the component, call
useWallet().connecton button click. - For ethers.js: install
ethers, create aBrowserProvider, and callgetSigner.
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.







