Themes
Themes in EverShop control the visual presentation and user interface of your store. They contain React components that render the pages your customers see, while keeping business logic separate in extensions.
What are Themes?
Themes provide:
Visual design and styling with Tailwind CSS
React components for pages and UI elements
Responsive layouts for mobile and desktop
Brand customization (colors, fonts, logos)
User experience enhancements
Extensions vs Themes : Extensions handle business logic (APIs, GraphQL, cron jobs), while themes handle presentation (React components, styling). This separation allows you to change your store’s appearance without affecting functionality.
Directory Structure
Themes live in the themes/ directory:
themes/[theme-name]/
├── src/
│ └── pages/ # Page components organized by area
│ ├── all/ # Components for all pages (Header, Footer)
│ ├── homepage/ # Homepage-specific components
│ ├── account/ # Account pages (Login, Register, Dashboard)
│ └── [area]/ # Other page areas
├── dist/ # Compiled files (auto-generated)
├── package.json
└── tsconfig.json
Activating a Theme
Set your active theme in config/default.json:
{
"system" : {
"theme" : "anasuplements"
}
}
You can only have one active theme at a time. The theme name must match a directory in themes/.
Creating a Theme
Create the directory structure
mkdir -p themes/myTheme/src/pages
{
"name" : "myTheme" ,
"version" : "1.0.0" ,
"private" : true ,
"main" : "src/index.ts" ,
"scripts" : {
"build" : "evershop build:theme"
},
"devDependencies" : {
"@types/react" : "^19.0.0"
}
}
{
"extends" : "../../tsconfig.json" ,
"compilerOptions" : {
"outDir" : "./dist" ,
"rootDir" : "./src" ,
"jsx" : "react"
},
"include" : [ "src/**/*" ]
}
Update config/default.json:
{
"system" : {
"theme" : "myTheme"
}
}
Page Components
Page components are React components with special exports that tell EverShop where and when to render them.
Basic Component Structure
Every theme component needs:
A default export (the React component)
A layout export (defines placement and order)
Optional query export (for GraphQL data fetching)
themes/anasuplements/src/pages/homepage/Hero.tsx
import React from 'react' ;
export default function Hero () {
return (
< section className = "bg-[#F8FAF9] py-16" >
< div className = "container mx-auto px-4" >
< div className = "text-center" >
< h1 className = "text-4xl md:text-5xl font-bold text-[#2D5A3D] mb-4" >
Bienvenido a Ana's Suplements
</ h1 >
< p className = "text-lg text-[#4A5568] mb-8 max-w-2xl mx-auto" >
Los mejores suplementos para tu salud y bienestar.
</ p >
< div className = "flex justify-center gap-4" >
< a
href = "/catalog"
className = "bg-[#2D5A3D] text-white px-8 py-3 rounded-lg hover:bg-[#1E3D2A] transition-colors font-semibold"
>
Ver Catálogo
</ a >
< a
href = "/contact"
className = "border-2 border-[#2D5A3D] text-[#2D5A3D] px-8 py-3 rounded-lg hover:bg-[#2D5A3D] hover:text-white transition-colors font-semibold"
>
Contactar
</ a >
</ div >
</ div >
</ div >
</ section >
);
}
export const layout = {
areaId: 'content' ,
sortOrder: 1
};
Layout Export
The layout export determines where the component appears:
export const layout = {
areaId: 'content' , // Which area to render in
sortOrder: 10 // Rendering order (lower numbers render first)
};
Common areaId values:
header - Top of all pages
footer - Bottom of all pages
content - Main content area
productPageBottom - Below product details
checkoutPaymentMethod - Payment method selection area
GraphQL Data Fetching
Use the query export to fetch data:
themes/anasuplements/src/pages/all/Header.tsx
import React from 'react' ;
type HeaderProps = {
storeName ?: string ;
categories ?: {
name : string ;
url : string ;
}[];
};
export default function Header ({ storeName = "Ana's Suplements" , categories = [] } : HeaderProps ) {
return (
< header className = "bg-[#F8FAF9] border-b border-[#E8F5E9]" >
< div className = "container mx-auto px-4 py-4" >
< div className = "flex items-center justify-between" >
< a href = "/" className = "text-2xl font-bold text-[#2D5A3D]" >
{ storeName }
</ a >
< nav className = "hidden md:flex space-x-6" >
{ categories . map (( category , index ) => (
< a
key = { index }
href = { category . url }
className = "text-[#4A5568] hover:text-[#2D5A3D] transition-colors"
>
{ category . name }
</ a >
)) }
</ nav >
< div className = "flex items-center space-x-4" >
< a href = "/cart" className = "text-[#2D5A3D] hover:text-[#1E3D2A]" >
{ /* Cart icon SVG */ }
</ a >
< a href = "/account" className = "text-[#2D5A3D] hover:text-[#1E3D2A]" >
{ /* Account icon SVG */ }
</ a >
</ div >
</ div >
</ div >
</ header >
);
}
export const layout = {
areaId: 'header' ,
sortOrder: 1
};
export const query = `
query Query {
storeName: setting(key: "storeName")
categories {
name
url
}
}
` ;
Page Areas
Organize components by the pages they appear on:
Global Components (all/)
Components that appear on every page:
src/pages/all/
├── Header.tsx
└── Footer.tsx
Homepage Components (homepage/)
Components specific to the homepage:
themes/anasuplements/src/pages/homepage/FeaturedProducts.tsx
import React from 'react' ;
type ProductCardProps = {
product : {
id : string ;
name : string ;
price : number ;
image ?: string ;
};
};
function ProductCard ({ product } : ProductCardProps ) {
return (
< div className = "bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow border border-[#E8F5E9] overflow-hidden" >
< div className = "aspect-square bg-[#F8FAF9] flex items-center justify-center" >
{ product . image ? (
< img src = { product . image } alt = { product . name } className = "w-full h-full object-cover" />
) : (
< svg className = "w-16 h-16 text-[#A0C4B0]" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 1 } d = "M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</ svg >
) }
</ div >
< div className = "p-4" >
< h3 className = "font-semibold text-[#2D5A3D] mb-2 line-clamp-2" >
{ product . name }
</ h3 >
< div className = "flex items-center justify-between" >
< span className = "text-lg font-bold text-[#2D5A3D]" >
$ { product . price ?. toFixed ( 2 ) }
</ span >
< button className = "bg-[#2D5A3D] text-white px-4 py-2 rounded text-sm hover:bg-[#1E3D2A] transition-colors" >
Agregar
</ button >
</ div >
</ div >
</ div >
);
}
export default function FeaturedProducts () {
const products = [
{ id: '1' , name: 'Vitamina D3' , price: 15.99 },
{ id: '2' , name: 'Omega-3' , price: 24.99 },
{ id: '3' , name: 'Proteína Whey' , price: 45.99 },
{ id: '4' , name: 'Magnesio' , price: 18.99 },
];
return (
< section className = "py-16" >
< div className = "container mx-auto px-4" >
< h2 className = "text-3xl font-bold text-center text-[#2D5A3D] mb-4" >
Productos Destacados
</ h2 >
< p className = "text-center text-[#4A5568] mb-8" >
Los suplementos más populares de nuestra tienda
</ p >
< div className = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6" >
{ products . map (( product ) => (
< ProductCard key = { product . id } product = { product } />
)) }
</ div >
</ div >
</ section >
);
}
export const layout = {
areaId: 'content' ,
sortOrder: 10
};
Account Pages (account/)
User account-related components:
src/pages/account/
├── Login.tsx
├── Register.tsx
└── Dashboard.tsx
Styling with Tailwind CSS
EverShop uses Tailwind CSS for styling. The Ana’s Suplements theme uses a custom color palette:
{ /* Primary green */ }
className = "text-[#2D5A3D] bg-[#2D5A3D]"
{ /* Hover state */ }
className = "hover:bg-[#1E3D2A]"
{ /* Background */ }
className = "bg-[#F8FAF9]"
{ /* Borders */ }
className = "border border-[#E8F5E9]"
{ /* Text colors */ }
className = "text-[#4A5568]"
Use Tailwind’s utility classes for responsive design:
sm: - Small screens (640px+)
md: - Medium screens (768px+)
lg: - Large screens (1024px+)
xl: - Extra large screens (1280px+)
TypeScript Props
Always define TypeScript types for your component props:
type MyComponentProps = {
title : string ;
description ?: string ; // Optional prop
items : Array <{
id : string ;
name : string ;
}>;
onSubmit ?: ( data : FormData ) => void ;
};
export default function MyComponent ({
title ,
description ,
items ,
onSubmit
} : MyComponentProps ) {
// Component implementation
}
Accessing EverShop Hooks
EverShop provides React hooks for common operations:
import { useCheckout } from '@evershop/evershop' ;
export default function PaymentMethod () {
const checkout = useCheckout ();
const { paymentMethods , setPaymentMethods } = checkout ;
// Use checkout data and methods
}
Best Practices
Use semantic component names
Name components based on what they represent, not how they look:
Good: ProductCard, HeroSection, FeaturedProducts
Bad: GreenBox, BigText, ThreeColumns
Keep components focused and reusable
Break large components into smaller, reusable pieces: // Instead of one large component
< ProductGrid />
// Break it down
< ProductGrid >
< ProductCard />
< ProductCard />
</ ProductGrid >
Use Tailwind classes directly
Avoid custom CSS files. Use Tailwind utility classes for all styling: // Good
< div className = "bg-white rounded-lg shadow-md p-6" >
// Avoid
< div className = "custom-card-style" >
Maintain consistent spacing
Use Tailwind’s spacing scale consistently:
p-4, p-6, p-8 for padding
mb-4, mb-6, mb-8 for margins
gap-4, gap-6, gap-8 for grid/flex gaps
anasuplements Theme Example
The included Ana’s Suplements theme demonstrates:
Custom color palette : Green (#2D5A3D) with pearl white backgrounds
Responsive design : Mobile-first layouts that adapt to larger screens
Component organization : Logical separation of homepage, account, and global components
TypeScript types : Proper type definitions for all props
Tailwind styling : Consistent use of utility classes
├── src/pages/
│ ├── all/
│ │ ├── Header.tsx # Global navigation
│ │ └── Footer.tsx # Global footer
│ ├── homepage/
│ │ ├── Hero.tsx # Hero banner
│ │ ├── Features.tsx # Feature highlights
│ │ └── FeaturedProducts.tsx # Product showcase
│ └── account/
│ ├── Login.tsx # Login form
│ ├── Register.tsx # Registration form
│ └── Dashboard.tsx # Account dashboard
├── package.json
└── tsconfig.json
Next Steps