Astro — неочевидный, но мощный выбор для небольшого и среднего интернет-магазина. Статические каталоги загружаются мгновенно (PageSpeed 100), а интерактивные элементы — корзина, фильтры, поиск — реализуются через изолированные острова. Разбираем архитектуру e-commerce на Astro.
Когда Astro подходит для магазина?
✅ Подходит:
- Небольшой магазин (до 5 000 SKU)
- Контент-тяжёлые каталоги: мебель, одежда, электроника с большими описаниями
- Когда SEO важен (органический трафик)
- Лендинг + магазин на одной платформе
❌ Лучше Next.js/Nuxt:
- Персонализация в реальном времени (рекомендации на основе истории)
- Корзина с привязкой к авторизованному пользователю (мультиустройство)
- Крупный магазин (50 000+ SKU, частые обновления цен)
Архитектура
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 для товаров
// 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
}),
}); Фронтматтер товара
---
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-фреймворками:
npm install nanostores @nanostores/preact // 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-остров
// 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>
);
} В странице товара:
---
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}
/> Фильтры каталога
Реализуйте фильтрацию через URL-параметры (?category=мебель&price=5000-15000).
Каждая комбинация фильтров — отдельная статическая страница (SSG) или Preact-остров.
Поиск товаров
Для небольших каталогов — Fuse.js (fuzzy search на клиенте, без сервера). Для больших — Algolia или MeiliSearch с Preact-интерфейсом.
Изображения
Astro <Image /> автоматически создаёт WebP, srcset для ретины и placeholder
blur. Это критично для конверсии: медленные фото = уход.
Интеграция платёжной системы
Stripe (международные платежи)
// 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 (российские платежи)
// src/pages/api/yukassa-checkout.ts
// Аналогично через API ЮKassa — см. официальную документацию
// https://yookassa.ru/developers/api SEO для каталога
Для каждого товара — rich snippets (разметка продукта):
---
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.