Voltar para o blog
E-commerce

VTEX IO: Criando Componentes Custom com React

20 de agosto de 202514 min de leituraPor Equipe GenStack
VTEXReactE-commerce

VTEX IO permite criar componentes React customizados e reutilizáveis. Vamos do setup à publicação.

O Que é VTEX IO?

VTEX IO é a plataforma de desenvolvimento da VTEX que permite:

- **Componentes React:** Frontend moderno

- **GraphQL:** Queries e mutations tipadas

- **Serverless:** Node services escaláveis

- **CDN Global:** Performance mundial

- **Hot Reload:** Desenvolvimento ágil

Setup Inicial

1. Instalar VTEX Toolbelt

```bash

npm install -g vtex

vtex login conta-vtex

```

2. Criar Workspace de Desenvolvimento

```bash

vtex use workspace-dev

```

3. Criar App Custom

```bash

vtex init

# Selecione: store > react-app-template

```

Estrutura de um App VTEX IO

```

my-custom-app/

├── manifest.json

├── store/

│ └── interfaces.json

├── react/

│ ├── package.json

│ ├── tsconfig.json

│ └── components/

│ └── CustomComponent.tsx

└── graphql/

└── schema.graphql

```

Exemplo 1: Componente de Banner Dinâmico

manifest.json

```json

{

"name": "custom-banner",

"vendor": "minhaloja",

"version": "0.1.0",

"title": "Custom Banner",

"description": "Banner dinâmico personalizável",

"builders": {

"react": "3.x",

"store": "0.x",

"docs": "0.x"

},

"dependencies": {

"vtex.store": "2.x",

"vtex.css-handles": "1.x"

},

"$schema": "https://raw.githubusercontent.com/vtex/node-vtex-api/master/gen/manifest.schema"

}

```

store/interfaces.json

```json

{

"custom-banner": {

"component": "CustomBanner",

"composition": "children",

"allowed": "*"

}

}

```

react/components/CustomBanner.tsx

```typescript

import React from 'react';

import { useCssHandles } from 'vtex.css-handles';

interface CustomBannerProps {

title: string;

subtitle?: string;

imageUrl: string;

ctaText: string;

ctaLink: string;

backgroundColor?: string;

}

const CSS_HANDLES = [

'bannerContainer',

'bannerImage',

'bannerContent',

'bannerTitle',

'bannerSubtitle',

'bannerCta',

] as const;

const CustomBanner: React.FC<CustomBannerProps> = ({

title,

subtitle,

imageUrl,

ctaText,

ctaLink,

backgroundColor = '#000',

}) => {

const handles = useCssHandles(CSS_HANDLES);

return (

<div

className={handles.bannerContainer}

style={{ backgroundColor }}

>

<img

src={imageUrl}

alt={title}

className={handles.bannerImage}

/>

<div className={handles.bannerContent}>

<h2 className={handles.bannerTitle}>{title}</h2>

{subtitle && (

<p className={handles.bannerSubtitle}>{subtitle}</p>

)}

<a

href={ctaLink}

className={handles.bannerCta}

>

{ctaText}

</a>

</div>

</div>

);

};

// Schema para Site Editor

CustomBanner.schema = {

title: 'Custom Banner',

description: 'Banner personalizável',

type: 'object',

properties: {

title: {

title: 'Título',

type: 'string',

default: 'Bem-vindo',

},

subtitle: {

title: 'Subtítulo',

type: 'string',

},

imageUrl: {

title: 'URL da Imagem',

type: 'string',

widget: {

'ui:widget': 'image-uploader',

},

},

ctaText: {

title: 'Texto do Botão',

type: 'string',

default: 'Comprar agora',

},

ctaLink: {

title: 'Link do Botão',

type: 'string',

default: '#',

},

backgroundColor: {

title: 'Cor de Fundo',

type: 'string',

widget: {

'ui:widget': 'color',

},

},

},

};

export default CustomBanner;

```

Estilização com CSS Handles

```css

/* styles.css */

.bannerContainer {

position: relative;

width: 100%;

min-height: 400px;

display: flex;

align-items: center;

justify-content: center;

overflow: hidden;

}

.bannerImage {

position: absolute;

width: 100%;

height: 100%;

object-fit: cover;

z-index: 0;

}

.bannerContent {

position: relative;

z-index: 1;

text-align: center;

color: white;

padding: 2rem;

}

.bannerTitle {

font-size: 3rem;

font-weight: bold;

margin-bottom: 1rem;

}

.bannerSubtitle {

font-size: 1.5rem;

margin-bottom: 2rem;

}

.bannerCta {

display: inline-block;

padding: 1rem 2rem;

background: #fff;

color: #000;

text-decoration: none;

border-radius: 4px;

font-weight: bold;

transition: transform 0.2s;

}

.bannerCta:hover {

transform: scale(1.05);

}

```

Exemplo 2: Componente com GraphQL

graphql/schema.graphql

```graphql

type Product {

id: ID!

name: String!

price: Float!

image: String

}

type Query {

featuredProducts(limit: Int): [Product]

}

```

react/components/FeaturedProducts.tsx

```typescript

import React from 'react';

import { useQuery } from 'react-apollo';

import gql from 'graphql-tag';

const GET_FEATURED_PRODUCTS = gql`

query FeaturedProducts($limit: Int) {

featuredProducts(limit: $limit) {

id

name

price

image

}

}

`;

interface FeaturedProductsProps {

limit?: number;

}

const FeaturedProducts: React.FC<FeaturedProductsProps> = ({ limit = 4 }) => {

const { data, loading, error } = useQuery(GET_FEATURED_PRODUCTS, {

variables: { limit },

});

if (loading) return <div>Carregando...</div>;

if (error) return <div>Erro ao carregar produtos</div>;

return (

<div className="featured-products">

<h2>Produtos em Destaque</h2>

<div className="products-grid">

{data.featuredProducts.map((product: any) => (

<div key={product.id} className="product-card">

<img src={product.image} alt={product.name} />

<h3>{product.name}</h3>

<p>R$ {product.price.toFixed(2)}</p>

</div>

))}

</div>

</div>

);

};

FeaturedProducts.schema = {

title: 'Produtos em Destaque',

type: 'object',

properties: {

limit: {

title: 'Quantidade de Produtos',

type: 'number',

default: 4,

},

},

};

export default FeaturedProducts;

```

Testando Localmente

1. Link o app

```bash

vtex link

```

2. Acesse seu workspace

```

https://workspace-dev--conta.myvtex.com

```

3. Adicione no Site Editor

1. Acesse Admin VTEX

2. Storefront > Site Editor

3. Adicione seu componente na página

Publicando o App

1. Versione o app

```bash

vtex release patch stable

```

2. Publique

```bash

vtex publish

```

3. Instale em produção

```bash

vtex install vendor.app-name@version

```

4. Promova workspace (ou vá direto para master)

```bash

vtex workspace promote

# ou

vtex use master

vtex install vendor.app-name@version

```

Boas Práticas

1. TypeScript Sempre

✅ Use tipos:

```typescript

interface Props {

title: string;

items: Array<{

id: string;

name: string;

}>;

}

```

2. CSS Handles para Customização

✅ Permita que lojistas customizem:

```typescript

const handles = useCssHandles(['container', 'title', 'item']);

```

3. Schema Completo

✅ Facilite configuração no Site Editor:

```typescript

Component.schema = {

title: 'Nome Amigável',

description: 'Descrição clara',

properties: {

// Todos os props configuráveis

},

};

```

4. Performance

✅ Otimize imagens:

```typescript

import { Image } from 'vtex.store-image';

<Image

src={imageUrl}

width={800}

height={600}

loading="lazy"

/>

```

5. Testes

✅ Teste em múltiplos dispositivos:

- Desktop

- Tablet

- Mobile

Troubleshooting

Erro: "Builder not found"

**Solução:** Adicione o builder no manifest.json

Erro: "Schema validation failed"

**Solução:** Verifique sintaxe do schema.graphql

Hot reload não funciona

**Solução:** Reinicie o vtex link

Componente não aparece no Site Editor

**Solução:** Verifique interfaces.json

Recursos

- **Documentação:** https://developers.vtex.com/

- **Store Framework:** https://developers.vtex.com/docs/guides/vtex-io-documentation-what-is-vtex-store-framework

- **CSS Handles:** https://developers.vtex.com/docs/guides/vtex-io-documentation-using-css-handles-for-store-customization

- **GraphQL:** https://developers.vtex.com/docs/guides/graphql-ide

Conclusão

VTEX IO oferece um ecossistema poderoso para criar componentes customizados:

- **React moderno:** Hooks, TypeScript, CSS-in-JS

- **Performance:** CDN global, code splitting automático

- **DX excelente:** Hot reload, type safety

- **Lojista-friendly:** Site Editor drag-and-drop

Próximos passos:

1. Crie seu primeiro componente simples

2. Teste no workspace de desenvolvimento

3. Adicione GraphQL se precisar de dados

4. Publique e compartilhe

Precisa de ajuda com VTEX IO? [Entre em contato](/contato).

Compartilhar:

Precisa de ajuda com seu projeto?

Entre em contato e vamos criar uma solução sob medida para você.

Falar com especialista