Admin components provide the building blocks for creating powerful admin interfaces in EverShop.
Layout Components
PageHeading
Displays page title with optional back button and action areas.
Location: packages/evershop/src/components/admin/PageHeading.tsx
import { PageHeading } from '@components/admin/PageHeading.js';
function MyAdminPage() {
return (
<PageHeading
heading="Edit Product"
backUrl="/admin/products"
/>
);
}
Props:
URL for back button navigation
Areas:
pageHeadingLeft: Components on the left side (back button, title)
pageHeadingRight: Components on the right side (action buttons)
Example with Actions:
import Area from '@components/common/Area.js';
import { Button } from '@components/common/ui/Button.js';
<PageHeading heading="Products" />
<Area id="pageHeadingRight" coreComponents={[
{
component: { default: () => (
<Button variant="default">Add Product</Button>
)},
sortOrder: 10
}
]} />
NavigationItem
Sidebar navigation menu items.
Location: packages/evershop/src/components/admin/NavigationItem.tsx
import { NavigationItem } from '@components/admin/NavigationItem.js';
<NavigationItem
url="/admin/products"
icon={<ProductIcon />}
title="Products"
/>
NavigationItemGroup
Groups related navigation items together.
Location: packages/evershop/src/components/admin/NavigationItemGroup.tsx
import { NavigationItemGroup } from '@components/admin/NavigationItemGroup.js';
<NavigationItemGroup title="Catalog">
<NavigationItem url="/admin/products" title="Products" />
<NavigationItem url="/admin/categories" title="Categories" />
</NavigationItemGroup>
Data Display Components
Status
Displays status badges for active/inactive states.
Location: packages/evershop/src/components/admin/Status.tsx
import { Status } from '@components/admin/Status.js';
<Status status={1} /> {/* Active */}
<Status status={0} /> {/* Inactive */}
Props:
Status value: 1 for active (green badge), 0 for inactive (red badge)
Output:
- Status
1: Green “Active” badge
- Status
0: Red “Inactive” badge
Thumbnail
Displays thumbnail images in grid views.
Location: packages/evershop/src/components/admin/grid/Thumbnail.tsx
import { Thumbnail } from '@components/admin/grid/Thumbnail.js';
<Thumbnail
src="/products/image.jpg"
alt="Product thumbnail"
width={60}
height={60}
/>
Pagination controls for data grids.
Location: packages/evershop/src/components/admin/grid/GridPagination.tsx
import { GridPagination } from '@components/admin/grid/GridPagination.js';
<GridPagination
currentPage={1}
totalPages={10}
onPageChange={(page) => navigate(`?page=${page}`)}
/>
ImageUploader
Powerful image upload component with drag-and-drop, multiple images, and image sorting.
Location: packages/evershop/src/components/admin/ImageUploader.tsx
import { ImageUploader } from '@components/admin/ImageUploader.js';
import { useState } from 'react';
function ProductForm() {
const [images, setImages] = useState([
{
uuid: '123',
url: '/uploads/image1.jpg',
path: '/uploads/image1.jpg'
}
]);
return (
<ImageUploader
currentImages={images}
isMultiple={true}
allowDelete={true}
allowSwap={true}
targetPath="products"
onUpload={(newImages) => {
console.log('Uploaded:', newImages);
setImages([...images, ...newImages]);
}}
onDelete={(image) => {
console.log('Deleted:', image);
setImages(images.filter(img => img.uuid !== image.uuid));
}}
onSortEnd={(oldIndex, newIndex) => {
console.log('Reordered:', oldIndex, newIndex);
}}
/>
);
}
Props:
Array of existing images. Each image has uuid, url, and optional path
Allow multiple images. When false, shows single image mode
Show delete button on images
Enable drag-and-drop reordering of images
Upload directory path (e.g., “products”, “categories”)
onUpload
(images: Image[]) => void | Promise<void>
Callback when images are uploaded
onDelete
(image: Image) => void | Promise<void>
Callback when an image is deleted
onSortEnd
(oldIndex: number, newIndex: number) => void
Callback when images are reordered
Single Image Mode:
<ImageUploader
currentImages={productImage ? [productImage] : []}
isMultiple={false}
onUpload={(images) => setProductImage(images[0])}
/>
CategorySelector
Select categories with search and autocomplete.
Location: packages/evershop/src/components/admin/CategorySelector.tsx
import { CategorySelector } from '@components/admin/CategorySelector.js';
<CategorySelector
name="categoryId"
label="Category"
selectedCategory={selectedCategory}
onSelect={(category) => setSelectedCategory(category)}
/>
ProductSelector
Select products with search functionality.
Location: packages/evershop/src/components/admin/ProductSelector.tsx
import { ProductSelector } from '@components/admin/ProductSelector.js';
<ProductSelector
name="relatedProducts"
label="Related Products"
selectedProducts={relatedProducts}
onSelect={(products) => setRelatedProducts(products)}
multiple={true}
/>
CollectionSelector
Select product collections.
Location: packages/evershop/src/components/admin/CollectionSelector.tsx
import { CollectionSelector } from '@components/admin/CollectionSelector.js';
<CollectionSelector
name="collectionId"
label="Collection"
selectedCollection={collection}
onSelect={(collection) => setCollection(collection)}
/>
AttributeGroupSelector
Select attribute groups for product attributes.
Location: packages/evershop/src/components/admin/AttributeGroupSelector.tsx
import { AttributeGroupSelector } from '@components/admin/AttributeGroupSelector.js';
<AttributeGroupSelector
name="attributeGroupId"
label="Attribute Group"
selectedGroup={attributeGroup}
onSelect={(group) => setAttributeGroup(group)}
/>
FileBrowser
Browse and select files from the media library.
Location: packages/evershop/src/components/admin/FileBrowser.tsx
import { FileBrowser } from '@components/admin/FileBrowser.js';
<FileBrowser
onSelect={(file) => {
console.log('Selected file:', file);
setSelectedFile(file);
}}
allowedTypes={['image/jpeg', 'image/png', 'image/webp']}
multiple={false}
/>
Standardized form action buttons (Save, Cancel).
Location: packages/evershop/src/components/admin/FormButtons.tsx
import { FormButtons } from '@components/admin/FormButtons.js';
<FormButtons
saveText="Save Product"
cancelUrl="/admin/products"
isLoading={isSubmitting}
/>
Utility Components
Spinner
Loading spinner for async operations.
Location: packages/evershop/src/components/admin/Spinner.jsx
import Spinner from '@components/admin/Spinner.js';
<Spinner width={40} height={40} />
Props:
ProductListSkeleton
Skeleton loader for product lists.
Location: packages/evershop/src/components/admin/ProductListSkeleton.tsx
import { ProductListSkeleton } from '@components/admin/ProductListSkeleton.js';
<ProductListSkeleton count={5} />
ImageUploaderSkeleton
Skeleton loader for image uploader.
Location: packages/evershop/src/components/admin/ImageUploaderSkeleton.tsx
import { ImageUploaderSkeleton } from '@components/admin/ImageUploaderSkeleton.js';
<ImageUploaderSkeleton itemCount={5} />
Grid Components
Sortable column header for data tables.
Location: packages/evershop/src/components/admin/grid/header/Sortable.tsx
import { Sortable } from '@components/admin/grid/header/Sortable.js';
<Sortable
column="name"
label="Product Name"
currentSort={{ column: 'name', direction: 'asc' }}
onSort={(column, direction) => handleSort(column, direction)}
/>
Complete Example
Here’s a complete admin page example:
import { PageHeading } from '@components/admin/PageHeading.js';
import { Form } from '@components/common/form/Form.js';
import { InputField } from '@components/common/form/InputField.js';
import { SelectField } from '@components/common/form/SelectField.js';
import { ImageUploader } from '@components/admin/ImageUploader.js';
import { CategorySelector } from '@components/admin/CategorySelector.js';
import { FormButtons } from '@components/admin/FormButtons.js';
import { useState } from 'react';
export default function ProductEditPage() {
const [images, setImages] = useState([]);
const [category, setCategory] = useState(null);
return (
<div>
<PageHeading
heading="Edit Product"
backUrl="/admin/products"
/>
<Form
action="/api/products/save"
method="POST"
onSuccess={() => {
window.location.href = '/admin/products';
}}
>
<InputField
name="name"
label="Product Name"
required
placeholder="Enter product name"
/>
<CategorySelector
name="categoryId"
label="Category"
selectedCategory={category}
onSelect={setCategory}
/>
<ImageUploader
currentImages={images}
isMultiple={true}
onUpload={(newImages) => setImages([...images, ...newImages])}
onDelete={(image) => setImages(images.filter(i => i.uuid !== image.uuid))}
/>
<SelectField
name="status"
label="Status"
options={[
{ value: '1', label: 'Active' },
{ value: '0', label: 'Inactive' }
]}
/>
<FormButtons
saveText="Save Product"
cancelUrl="/admin/products"
/>
</Form>
</div>
);
}
Common Components
Explore shared components
Creating Components
Learn to build custom components