Разработка фронтенда dApp на Next.js

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка фронтенда dApp на Next.js
Средний
~1-2 недели
Часто задаваемые вопросы

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

Этапы блокчейн-разработки

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

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

Разработка фронтенда dApp на Next.js

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

Архитектура: разделение server и client

Проблема гидрации с 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 протокола, список токенов, цены) можно загружать на сервере. Next.js 14 Server Components + 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 14 кэшируется по умолчанию. Для on-chain данных через viem нужно явное управление: revalidate: 60 в export const revalidate или ручная инвалидация через Route Handlers.

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

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.

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

.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.

Стек

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

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

Базовый dApp frontend с wallet connection, чтением контракта и транзакционными формами — 1 неделя. С SSR для публичных данных, оптимистичными обновлениями, полным lifecycle транзакций и мобильной адаптацией — 2 недели.