Разработка фронтенда dApp на Next.js: Web3-интеграция и SSR

Представьте: ваш DeFi-дашборд загружается 5 секунд из-за 20 отдельных RPC-запросов. Каждый запрос к блокчейну — это задержка. Мы решаем это через multicall, объединяя все вызовы в один. Итог — загрузка падает до 0.5 секунды. Это типичная задача, которую мы решаем каждый день для клиентов. За 5 лет р

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    998
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1267
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1003

Представьте: ваш DeFi-дашборд загружается 5 секунд из-за 20 отдельных RPC-запросов. Каждый запрос к блокчейну — это задержка. Мы решаем это через multicall, объединяя все вызовы в один. Итог — загрузка падает до 0.5 секунды. Это типичная задача, которую мы решаем каждый день для клиентов. За 5 лет работы над 50+ dApp мы выработали подход, который гарантирует быструю загрузку при сложной Web3-логике. В этой статье — конкретные решения по интеграции, оптимизации и разделению компонентов.

Next.js для Web3 — это прежде всего решение конкретного противоречия: блокчейн данные требуют client-side execution (wallet connection, подпись транзакций), а SEO и начальная загрузка требуют серверного рендеринга. Неправильная граница между server и client компонентами ломает либо wallet integration, либо производительность. Next.js документация рекомендует использовать Server Components для данных, не требующих интерактивности.

Как правильно разделить Server и Client компоненты в dApp?

Ошибка на этом этапе приводит к hydration mismatch или падению приложения. Рассмотрим решение.

Проблема гидрации с wagmi/viem

wagmi 2.x использует localStorage и window.ethereum — оба объекта недоступны на сервере. Наивный импорт useAccount в Server Component вызывает ошибку. Ещё хуже — hydration mismatch: сервер рендерит «не подключён», клиент после гидрации показывает «подключён к MetaMask», и React выдаёт warning или ломает UI.

Правильная структура:

// app/providers.tsx — CLIENT компонент, оборачивает всё приложение 'use client'; import { WagmiProvider, createConfig, http } from 'wagmi'; import { mainnet, base, arbitrum } from 'wagmi/chains'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ConnectKitProvider } from 'connectkit'; const config = createConfig({ chains: [mainnet, base, arbitrum], transports: { [mainnet.id]: http(process.env.NEXT_PUBLIC_RPC_MAINNET), [base.id]: http(process.env.NEXT_PUBLIC_RPC_BASE), [arbitrum.id]: http(process.env.NEXT_PUBLIC_RPC_ARBITRUM), }, }); const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { return ( <WagmiProvider config={config}> <QueryClientProvider client={queryClient}> <ConnectKitProvider>{children}</ConnectKitProvider> </QueryClientProvider> </WagmiProvider> ); } 
// app/layout.tsx — SERVER компонент, импортирует Providers import { Providers } from './providers'; export default function RootLayout({ children }) { return ( <html> <body> <Providers>{children}</Providers> </body> </html> ); } 

Статичный контент (navbar, footer, landing text) — Server Components. Кошелёк, балансы, транзакционные кнопки — Client Components с 'use client'.

SSR для on-chain данных

Публичные данные блокчейна (TVL протокола, список токенов, цены) можно загружать на сервере. Server Components в Next.js (App Router) + fetch с caching:

// app/protocol/page.tsx — Server Component async function getProtocolStats() { const client = createPublicClient({ chain: mainnet, transport: http(process.env.RPC_URL), // приватная переменная, не NEXT_PUBLIC_ }); const [tvl, totalUsers] = await Promise.all([ client.readContract({ address: PROTOCOL, abi, functionName: 'getTVL' }), client.readContract({ address: PROTOCOL, abi, functionName: 'userCount' }), ]); return { tvl, totalUsers }; } export default async function ProtocolPage() { const stats = await getProtocolStats(); // выполняется на сервере return <StatsDisplay stats={stats} />; } 

fetchв Next.js кэшируется по умолчанию. Для on-chain данных через viem нужно явное управление: revalidate: 60 в export const revalidate или ручная инвалидация через Route Handlers.

Почему multicall ускоряет загрузку в 10 раз?

Управление состоянием транзакций

Lifecycle транзакции в UI: idle → preparing → signing → pending → confirming → success/error. Каждое состояние требует отдельного UI feedback. wagmi предоставляет хуки для каждого этапа:

function TransactionButton({ tokenId }: { tokenId: bigint }) { const { writeContract, data: hash, isPending, error } = useWriteContract(); const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash }); if (isPending) return <Button disabled>Подтвердите в кошельке...</Button>; if (isConfirming) return <Button disabled>Ожидание подтверждения ({hash?.slice(0, 8)}...)</Button>; if (isSuccess) return <Button variant="success">Готово ✓</Button>; return ( <Button onClick={() => writeContract({ address: CONTRACT, abi, functionName: 'mint', args: [tokenId] })} > Минт </Button> ); } 

Оптимистичные обновления

Для операций с предсказуемым результатом (like, follow, simple toggle) — оптимистичный UI через @tanstack/react-query useMutation с onMutate/onError/onSettled. Пользователь видит изменение немедленно, откат происходит только при ошибке.

Multicall и batch запросы

Для dashboard с множеством on-chain данных — никаких параллельных одиночных RPC вызовов. Multicall3 собирает все запросы в один:

const results = await publicClient.multicall({ contracts: tokenIds.map(id => ({ address: NFT_CONTRACT, abi: erc721Abi, functionName: 'tokenURI', args: [id], })), }); 

viem поддерживает multicall нативно. Для 100 токенов — 1 RPC запрос вместо 100. Это разница между 2 секундами и 200 миллисекундами загрузки dashboard. Server Components для статичных данных рендерятся в 3 раза быстрее Client Components, так как не включают JavaScript бандл для гидрации.

Алгоритм разработки dApp: от архитектуры до деплоя

  1. Анализ смарт-контракта и дизайн-макетов, определение точек интеграции.
  2. Проектирование компонентной архитектуры: Server vs Client, layout, роутинг.
  3. Реализация провайдера Web3 (wagmi + ConnectKit/RainbowKit).
  4. Написание серверных компонент для публичных on-chain данных с кэшированием.
  5. Реализация клиентских компонент: подключение кошелька, балансы, транзакции.
  6. Оптимизация запросов через multicall и React Query.
  7. Тестирование: unit (Vitest), e2e (Playwright), покрытие edge-кейсов транзакций.
  8. Деплой на Vercel или Docker, настройка CI/CD.

Важные детали окружения

.env.local для локальной разработки, .env.production для прода. Переменные без NEXT_PUBLIC_ не попадают в клиентский бандл — RPC URLs с API ключами должны быть без префикса (использовать только в Server Components или Route Handlers).

RPC провайдеры: Alchemy, Infura, QuickNode. Для продакшена — несколько провайдеров с fallback через wagmi fallback transport. Публичные RPC (вроде https://eth.llamarpc.com) имеют rate limits — не использовать в production без fallback. Средняя экономия на RPC-запросах существенна благодаря multicall и кэшированию.

Сравнение: Server Components vs Client Components для типичных блоков dApp

Блок Рекомендуемый тип Причина
Navbar, Footer, SEO-текст Server Component Нет зависимости от кошелька, быстрая загрузка
Кнопка подключения кошелька Client Component Требует window.ethereum
Баланс токенов Client Component Динамические данные, подписка
TVL протокола, список токенов Server Component (с кэшем) Публичные данные, улучшает SEO
Форма транзакции Client Component Взаимодействие с кошельком

Что входит в работу

  • Архитектура: выбор стратегии рендеринга (SSR/SSG/CSR), разбивка на Server/Client Components.
  • Интеграция кошельков: MetaMask, WalletConnect, Coinbase Wallet, Ledger.
  • Работа со смарт-контрактами: чтение и запись через viem, обработка lifecycle транзакций.
  • Оптимизация: multicall, кэширование, gas estimation, fallback RPC.
  • Тестирование: unit (Vitest), e2e (Playwright), покрытие edge-кейсов.
  • Документация: README, комментарии в коде, схема компонентов.
  • Поддержка: настройка CI/CD (Vercel/Docker), мониторинг через Tenderly.

Ориентиры по срокам

Этап Длительность
Базовый фронтенд (wallet + транзакции) от 1 недели
+ SSR + оптимизация + полный lifecycle от 2 недель
+ Кастомные UI-компоненты, анимации от 3 недель

Сроки уточняются после анализа вашего смарт-контракта и дизайн-макетов. Гарантируем прозрачность на каждом этапе. Свяжитесь с нами для консультации — обсудим ваш проект и предложим оптимальное решение.

Стек

Next.js (App Router), wagmi 2.x, viem 2.x, ConnectKit или RainbowKit, @tanstack/react-query 5.x, TypeScript, Tailwind CSS. Тестирование: Vitest + Playwright для e2e. Деплой: Vercel или Docker.

Закажите разработку прямо сейчас — получите консультацию по вашему проекту. Оценим объём и сложность, предложим оптимальное решение.