Next.js
Use Seatmap Canvas with Next.js (App Router & Pages Router).
Installation
npm install @alisaitteke/seatmap-canvas next react react-dom
- Next.js 13.0.0 or higher
- React 18.0.0 or higher
- TypeScript support included
This plugin supports both App Router (Next.js 13+) and Pages Router. Choose the import path that matches your setup.
App Router (Recommended)
The modern Next.js routing system with Server Components and Server Actions.
Client Component Usage
// app/venue/page.tsx
'use client';
import { SeatmapCanvas } from '@alisaitteke/seatmap-canvas/nextjs';
import '@alisaitteke/seatmap-canvas/dist/seatmap.canvas.css';
export default function VenuePage() {
const handleSeatClick = (seat: any) => {
if (seat.item.salable) {
seat.isSelected() ? seat.unSelect() : seat.select();
}
};
const blocks = [
{
id: 'block-1',
title: 'VIP Section',
color: '#01a5ff',
seats: [
{ id: 'seat-1', x: 50, y: 50, salable: true, title: 'A1' },
{ id: 'seat-2', x: 80, y: 50, salable: true, title: 'A2' },
],
},
];
return (
<div style={{ width: '100%', height: '600px' }}>
<SeatmapCanvas
data={blocks}
options={{ legend: true }}
onSeatClick={handleSeatClick}
/>
</div>
);
}
Server Component with Data Fetching
// app/venue/[id]/page.tsx
import { SeatmapServerWrapper } from '@alisaitteke/seatmap-canvas/nextjs/app-router';
import '@alisaitteke/seatmap-canvas/dist/seatmap.canvas.css';
export default async function VenuePage({ params }: { params: { id: string } }) {
return (
<div style={{ width: '100%', height: '600px' }}>
<SeatmapServerWrapper
dataSource={`${process.env.API_URL}/venues/${params.id}/seatmap`}
options={{ legend: true }}
revalidate={3600} // Cache for 1 hour
/>
</div>
);
}
Using Server Actions
'use client';
import { SeatmapCanvas } from '@alisaitteke/seatmap-canvas/nextjs/app-router';
import { reserveSeats } from '@alisaitteke/seatmap-canvas/nextjs/app-router';
import { useState } from 'react';
export default function VenueReservation({ venueId }: { venueId: string }) {
const [selectedSeats, setSelectedSeats] = useState<any[]>([]);
const handleReserve = async () => {
const seats = selectedSeats.map((seat) => ({
blockId: seat.block.id,
seatId: seat.id,
}));
const result = await reserveSeats(venueId, seats);
if (result.success) {
alert(`Reservation created: ${result.data?.reservationId}`);
}
};
return (
<>
<SeatmapCanvas
onSeatSelect={(seat) => setSelectedSeats([...selectedSeats, seat])}
onSeatUnselect={(seat) =>
setSelectedSeats(selectedSeats.filter((s) => s.id !== seat.id))
}
/>
<button onClick={handleReserve}>
Reserve {selectedSeats.length} seat(s)
</button>
</>
);
}
Using the Hook
'use client';
import { useSeatmap } from '@alisaitteke/seatmap-canvas/nextjs';
import { useEffect } from 'react';
export default function CustomSeatmap() {
const {
containerRef,
isReady,
selectedSeats,
loadData,
zoomToBlock,
addEventListener,
} = useSeatmap({ legend: true });
useEffect(() => {
if (isReady) {
loadData([
{
id: 'block-1',
title: 'Main Floor',
seats: [{ id: 'seat-1', x: 0, y: 0, salable: true }],
},
]);
}
}, [isReady]);
return (
<div>
<div>Selected: {selectedSeats.length} seats</div>
<button onClick={() => zoomToBlock('block-1')}>Zoom to Block 1</button>
<div ref={containerRef} style={{ width: '100%', height: '600px' }} />
</div>
);
}
Pages Router
The traditional Next.js routing system.
Basic Usage
// pages/venue/[id].tsx
import { SeatmapCanvas } from '@alisaitteke/seatmap-canvas/nextjs/pages-router';
import '@alisaitteke/seatmap-canvas/dist/seatmap.canvas.css';
import type { BlockData } from '@alisaitteke/seatmap-canvas/nextjs';
export default function VenuePage({ data }: { data: BlockData[] }) {
const handleSeatClick = (seat: any) => {
if (seat.item.salable) {
seat.isSelected() ? seat.unSelect() : seat.select();
}
};
return (
<div style={{ width: '100%', height: '600px' }}>
<SeatmapCanvas data={data} onSeatClick={handleSeatClick} />
</div>
);
}
Static Site Generation (SSG)
import type { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async ({ params }) => {
const res = await fetch(`${process.env.API_URL}/venues/${params?.id}/seatmap`);
const data = await res.json();
return {
props: { data },
};
};
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking',
};
}
Incremental Static Regeneration (ISR)
export const getStaticProps: GetStaticProps = async ({ params }) => {
const res = await fetch(`${process.env.API_URL}/venues/${params?.id}/seatmap`);
const data = await res.json();
return {
props: { data },
revalidate: 3600, // Revalidate every hour
};
};
Server-Side Rendering (SSR)
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const res = await fetch(`${process.env.API_URL}/venues/${params?.id}/seatmap`);
const data = await res.json();
return { props: { data } };
};
API Reference
Component Props (App Router & Pages Router)
| Prop | Type | Default | Description |
|---|---|---|---|
options | SeatmapOptions | {} | Configuration options |
data | BlockData[] | [] | Seat and block data |
className | string | '' | CSS class name |
style | React.CSSProperties | {} | Inline styles |
autoZoomToVenue | boolean | true | Auto zoom to fit venue |
onReady | (instance) => void | - | Callback when initialized |
onSeatClick | (seat) => void | - | Seat click handler |
onSeatSelect | (seat) => void | - | Seat selection handler |
onSeatUnselect | (seat) => void | - | Seat unselection handler |
onBlockClick | (block) => void | - | Block click handler |
Server Wrapper Props (App Router Only)
| Prop | Type | Default | Description |
|---|---|---|---|
dataSource | string | (() => Promise<BlockData[]>) | - | API URL or fetch function |
options | SeatmapOptions | {} | Configuration options |
fallback | ReactNode | - | Custom loading component |
revalidate | number | 3600 | Cache revalidation time (seconds) |
Server Actions (App Router Only)
import {
loadSeatmapData,
selectSeats,
reserveSeats,
cancelReservation,
getSeatAvailability,
} from '@alisaitteke/seatmap-canvas/nextjs/app-router';
loadSeatmapData(venueId: string)
Load seatmap data from API.
reserveSeats(venueId, seats, userId?)
Create a reservation.
cancelReservation(reservationId)
Cancel an existing reservation.
getSeatAvailability(venueId, blockId?)
Get real-time availability.
Utilities
import {
generateSeatmapStaticParams,
getStaticSeatmapProps,
generateSeatmapMetadata,
} from '@alisaitteke/seatmap-canvas/nextjs/app-router';
Common Issues
Hydration Errors
The component handles hydration automatically. If you encounter issues:
'use client';
import { useState, useEffect } from 'react';
export default function SafeSeatmap() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return <div>Loading...</div>;
return <SeatmapCanvas {...props} />;
}
Window/Document Not Defined
App Router: Ensure you're using 'use client' directive.
Pages Router: The dynamic import wrapper handles this automatically.
CSS Not Loading
Always import the CSS file:
import '@alisaitteke/seatmap-canvas/dist/seatmap.canvas.css';
TypeScript Types
import type {
SeatmapOptions,
SeatmapCanvasProps,
BlockData,
SeatData,
SeatClickEvent,
UseSeatmapReturn,
ServerActionResult,
} from '@alisaitteke/seatmap-canvas/nextjs';
Performance Optimization
1. Use Static Generation
For data that doesn't change frequently:
// App Router
export const revalidate = 3600; // 1 hour
// Pages Router
export async function getStaticProps() {
return {
props: { data },
revalidate: 3600,
};
}
2. Code Splitting
import dynamic from 'next/dynamic';
const SeatmapCanvas = dynamic(
() => import('@alisaitteke/seatmap-canvas/nextjs').then((mod) => mod.SeatmapCanvas),
{ ssr: false }
);
3. Server Components
Fetch data in Server Components to reduce client bundle:
async function VenueData({ id }: { id: string }) {
const data = await fetchVenueData(id);
return <SeatmapCanvas data={data} />;
}
Migration Guide
From React to Next.js
- Install Next.js:
npm install next
- Update imports:
// Before (React)
import { SeatmapCanvas } from '@alisaitteke/seatmap-canvas/react';
// After (Next.js App Router)
'use client';
import { SeatmapCanvas } from '@alisaitteke/seatmap-canvas/nextjs';
// After (Next.js Pages Router)
import { SeatmapCanvas } from '@alisaitteke/seatmap-canvas/nextjs/pages-router';
- Add
'use client'directive (App Router only)
Props remain the same - no code changes needed!
Examples
Check out complete examples:
Next Steps
- Explore API Reference
- Learn about Event Handling
- Check out Custom Shapes