Astro calendar_today 23 апр. 2026 г. schedule 2 мин

Интернет-магазин на Astro: E-commerce гайд 2026

Как создать интернет-магазин на Astro.js: каталог товаров, корзина на nanostores, интеграция Stripe и ЮKassa, фильтрация и поиск. Полный гайд с кодом.

person
Журналист
Автор
Интернет-магазин на Astro — e-commerce гайд 2026

Astro — неочевидный, но мощный выбор для небольшого и среднего интернет-магазина. Статические каталоги загружаются мгновенно (PageSpeed 100), а интерактивные элементы — корзина, фильтры, поиск — реализуются через изолированные острова. Разбираем архитектуру e-commerce на Astro.

Когда Astro подходит для магазина?

Подходит:

  • Небольшой магазин (до 5 000 SKU)
  • Контент-тяжёлые каталоги: мебель, одежда, электроника с большими описаниями
  • Когда SEO важен (органический трафик)
  • Лендинг + магазин на одной платформе

Лучше Next.js/Nuxt:

  • Персонализация в реальном времени (рекомендации на основе истории)
  • Корзина с привязкой к авторизованному пользователю (мультиустройство)
  • Крупный магазин (50 000+ SKU, частые обновления цен)

Архитектура

code
shop/
├── src/
│   ├── content/
│   │   └── products/          # MDX-карточки товаров
│   ├── stores/
│   │   └── cart.ts            # Nanostores — корзина
│   ├── components/
│   │   ├── ProductCard.astro
│   │   ├── CartButton.tsx     # Кнопка с кол-вом (Preact-остров)
│   │   ├── CartDrawer.tsx     # Корзина-шторка (Preact-остров)
│   │   └── AddToCart.tsx      # Кнопка "В корзину" (Preact-остров)
│   └── pages/
│       ├── index.astro         # Главная
│       ├── catalog/
│       │   ├── index.astro    # Каталог с фильтрами
│       │   └── [slug].astro   # Страница товара
│       └── checkout.astro     # Оформление заказа

Content Collection для товаров

code
// src/content.config.ts
const products = defineCollection({
  loader: glob({ pattern: '**/*.mdx', base: './src/content/products' }),
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      description: z.string(),
      price: z.number(), // В копейках/центах
      comparePrice: z.number().optional(), // Зачёркнутая цена
      sku: z.string(), // Артикул
      category: z.string(),
      tags: z.array(z.string()).default([]),
      images: z.array(image()), // Галерея
      inStock: z.boolean().default(true),
      featured: z.boolean().default(false),
      stripeProductId: z.string().optional(), // Stripe Product ID
    }),
});

Фронтматтер товара

code
---
title: 'Кресло-мешок Comfort Pro XL'
description: 'Мягкое кресло из экокожи. 120 кг нагрузки, чехол стирается'
price: 590000 # 5900 руб в копейках
comparePrice: 790000
sku: 'CHAIR-CP-XL'
category: 'Мебель'
tags: ['Кресла', 'Домашний офис', 'XL']
inStock: true
featured: true
stripeProductId: 'prod_xxxxx'
---

## О товаре

Кресло-мешок **Comfort Pro XL** создано для тех, кто проводит за работой...

## Характеристики

| Параметр | Значение     |
| -------- | ------------ |
| Нагрузка | до 120 кг    |
| Размер   | 100×80×80 см |
| Материал | Экокожа      |

Корзина на nanostores

nanostores — микробиблиотека состояния, работает со всеми UI-фреймворками:

code
npm install nanostores @nanostores/preact
code
// src/stores/cart.ts
import { atom, computed } from 'nanostores';

export interface CartItem {
  sku: string;
  title: string;
  price: number;
  quantity: number;
  image?: string;
}

// Основное хранилище
export const cartItems = atom<CartItem[]>([]);

// Производные значения
export const cartCount = computed(cartItems, (items) =>
  items.reduce((sum, item) => sum + item.quantity, 0),
);

export const cartTotal = computed(cartItems, (items) =>
  items.reduce((sum, item) => sum + item.price * item.quantity, 0),
);

// Действия
export function addToCart(item: Omit<CartItem, 'quantity'>) {
  const current = cartItems.get();
  const existing = current.find((i) => i.sku === item.sku);
  if (existing) {
    cartItems.set(
      current.map((i) => (i.sku === item.sku ? { ...i, quantity: i.quantity + 1 } : i)),
    );
  } else {
    cartItems.set([...current, { ...item, quantity: 1 }]);
  }
  // Сохранение в localStorage
  localStorage.setItem('cart', JSON.stringify(cartItems.get()));
}

export function removeFromCart(sku: string) {
  cartItems.set(cartItems.get().filter((i) => i.sku !== sku));
}

// Восстановление корзины при загрузке
if (typeof window !== 'undefined') {
  const saved = localStorage.getItem('cart');
  if (saved) cartItems.set(JSON.parse(saved));
}

Кнопка “В корзину” — Preact-остров

code
// src/components/AddToCart.tsx
import { addToCart } from '../stores/cart';

interface Props {
  sku: string;
  title: string;
  price: number;
  image?: string;
}

export default function AddToCart({ sku, title, price, image }: Props) {
  return (
    <button
      class="btn btn--primary"
      onClick={() => addToCart({ sku, title, price, image })}
    >
      В корзину
    </button>
  );
}

В странице товара:

code
---
import AddToCart from '../../components/AddToCart.tsx';
const { product } = Astro.props;
---

<AddToCart
  client:load
  sku={product.data.sku}
  title={product.data.title}
  price={product.data.price}
/>
filter_list

Фильтры каталога

Реализуйте фильтрацию через URL-параметры (?category=мебель&price=5000-15000). Каждая комбинация фильтров — отдельная статическая страница (SSG) или Preact-остров.

search

Поиск товаров

Для небольших каталогов — Fuse.js (fuzzy search на клиенте, без сервера). Для больших — Algolia или MeiliSearch с Preact-интерфейсом.

image

Изображения

Astro &lt;Image /&gt; автоматически создаёт WebP, srcset для ретины и placeholder blur. Это критично для конверсии: медленные фото = уход.

Интеграция платёжной системы

Stripe (международные платежи)

code
// src/pages/api/checkout.ts
import Stripe from 'stripe';

const stripe = new Stripe(import.meta.env.STRIPE_SECRET_KEY);

export async function POST({ request }) {
  const { items } = await request.json();

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    line_items: items.map((item) => ({
      price_data: {
        currency: 'rub',
        product_data: { name: item.title },
        unit_amount: item.price,
      },
      quantity: item.quantity,
    })),
    mode: 'payment',
    success_url: `${import.meta.env.PUBLIC_SITE_URL}/success`,
    cancel_url: `${import.meta.env.PUBLIC_SITE_URL}/checkout`,
  });

  return new Response(JSON.stringify({ url: session.url }));
}

ЮKassa (российские платежи)

code
// src/pages/api/yukassa-checkout.ts
// Аналогично через API ЮKassa — см. официальную документацию
// https://yookassa.ru/developers/api

SEO для каталога

Для каждого товара — rich snippets (разметка продукта):

code
---
const productSchema = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: product.data.title,
  description: product.data.description,
  sku: product.data.sku,
  offers: {
    '@type': 'Offer',
    price: (product.data.price / 100).toFixed(2),
    priceCurrency: 'RUB',
    availability: product.data.inStock
      ? 'https://schema.org/InStock'
      : 'https://schema.org/OutOfStock',
    url: Astro.url.href,
  },
};
---

<script type="application/ld+json" set:html={JSON.stringify(productSchema)} />

Это даёт звёздочки и цену прямо в выдаче Google — увеличивает CTR на 20-30%.

Итог

Astro для интернет-магазина — зрелый и эффективный выбор, если SEO-трафик важнее персонализации в реальном времени. nanostores для корзины, статические страницы товаров, Stripe/ЮKassa для оплаты — минималистичный, но функциональный стек. Полный обзор баз данных для хранения заказов — в статье Базы данных для Astro.

Портрет автора Дмитрий Соколов

Senior Frontend Engineer / Tech Writer

Senior Frontend Engineer с 9-летним опытом. Специализируется на Astro.js и JAMstack.

Комментарии

Загрузка комментариев...

Оставить комментарий

Комментарии проходят модерацию перед публикацией. Правила

Рекомендуем к прочтению