Self-Hosting Guide
Deploy and configure the self-hosted version of SeatSquirrel Designer and Picker
Overview
The self-hosted version of SeatSquirrel provides a complete, standalone seat map solution that you can deploy on your own infrastructure. It includes both the Designer (for creating layouts) and Picker (for seat selection) as static files that can be served from any web server.
Key Benefits:
- Complete control over your infrastructure
- No external dependencies on SeatSquirrel servers
- Data stays entirely within your systems
- Same SDK API as the hosted version
Self-Hosting Access
Distribution Contents
The self-hosted package contains a directory with the following structure:
── designer.html # Designer application entry point
── picker.html # Picker application entry point
── sdk/
├── designer-sdk.js # Designer embedding SDK
└── picker-sdk.js # Picker embedding SDK
── assets/
├── *.js # Bundled JavaScript modules
└── *.css # Bundled stylesheetsFiles Overview
| File | Purpose |
|---|---|
designer.html | The layout designer application |
picker.html | The seat selection/booking application |
assets/* | Required JS/CSS bundles (auto-loaded by HTML files) |
sdk/designer-sdk.js | JavaScript SDK for embedding the Designer, called from your webpage |
sdk/picker-sdk.js | JavaScript SDK for embedding the Picker, called from your webpage |
Deployment
The self-hosted version can be deployed to any static hosting platform or web server capable of serving static files (nginx, Apache, Caddy, etc.).
Static Hosting Guide
For detailed deployment instructions for various platforms (Netlify, Vercel, Cloudflare Pages, AWS S3, nginx, etc.), see the official Vite Static Deployment Guide.
Example: nginx Configuration
server {
listen 80;
server_name seatsquirrel.yourdomain.com;
root /var/www/seatsquirrel;
index designer.html;
location / {
try_files $uri $uri/ /designer.html;
}
# Cache static assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Example: Caddy Configuration
seatsquirrel.yourdomain.com {
root * /var/www/seatsquirrel
file_server
try_files {path} /designer.html
}Example: Running locally with Serve
- Install Serve:
pnpm install -g serve - Serve the files with
servefrom inside the directory:serve
This will start the server on port 3000.
Configuration
Domain Verification
The self-hosted version uses the SEATSQUIRREL_APPROVED_DOMAINS environment variable to control which domains are
allowed to embed the Designer and Picker. This is a security measure to prevent unauthorized embedding.
Important
Set the SEATSQUIRREL_APPROVED_DOMAINS environment variable before your application loads. The exact method depends
on your deployment platform. For example, in a .env file:
SEATSQUIRREL_APPROVED_DOMAINS=https://example.com,https://www.example.com,http://localhost:3000Format: Comma-separated list of full URLs with protocol
# Example: Allow embedding from these domains
SEATSQUIRREL_APPROVED_DOMAINS=https://example.com,https://www.example.com,http://localhost:3000Important Notes:
- Include the full URL with protocol (
https://orhttp://) - Include the port for localhost:
http://localhost:3000 - No trailing slashes
- Separate multiple domains with commas
Using the SDKs
The self-hosted SDKs work identically to the hosted version. You must set the baseUrl option to point to your
self-hosted deployment in the SDK constructor.
Designer SDK
<!-- Include the SDK from your self-hosted location -->
<script src="https://seatsquirrel.yourdomain.com/sdk/designer-sdk.js"></script>
<div id="designer-container" style="width: 100%; height: 700px;"></div>
<script>
const designer = new SeatSquirrel.Designer({
container: '#designer-container',
// Point to your self-hosted deployment
baseUrl: 'https://seatsquirrel.yourdomain.com',
onReady: function () {
console.log('Designer is ready');
},
onExport: function (layoutData) {
// Save the layout to your database
console.log('Layout exported:', layoutData);
saveToYourDatabase(layoutData);
},
onLayoutChanged: function (data) {
console.log('Has unsaved changes:', data.hasChanges);
},
onError: function (error) {
console.error('Designer error:', error);
},
});
</script>Picker SDK
<!-- Include the SDK from your self-hosted location -->
<script src="https://seatsquirrel.yourdomain.com/sdk/picker-sdk.js"></script>
<div id="picker-container" style="width: 100%; height: 600px;"></div>
<script>
const picker = new SeatSquirrel.StatelessPicker({
container: '#picker-container',
// Point to your self-hosted deployment
baseUrl: 'https://seatsquirrel.yourdomain.com',
onReady: function () {
// Load your layout data from your database
picker.loadLayout(layoutDataFromYourDatabase);
picker.setAvailability(availabilityFromYourDatabase);
},
onSelectionChange: function (data) {
console.log('Selected:', data.selections);
console.log('Total:', data.totals.amount);
},
onComplete: function (data) {
// Handle checkout - process the selections
console.log('Checkout requested:', data);
processCheckout(data.selections, data.totals);
},
onError: function (error) {
console.error('Picker error:', error);
},
});
</script>Complete Example
Here's a complete HTML page demonstrating both the Designer and Picker:
<!DOCTYPE html>
<html>
<head>
<title>SeatSquirrel Self-Hosted Demo</title>
<style>
.container {
width: 100%;
height: 700px;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
.toolbar {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
button {
padding: 10px 20px;
border-radius: 6px;
border: none;
cursor: pointer;
}
.primary {
background: #3b82f6;
color: white;
}
.secondary {
background: #e5e7eb;
color: #374151;
}
#status {
margin-left: 10px;
font-weight: 600;
}
</style>
</head>
<body>
<h1>Venue Layout Designer</h1>
<div class="toolbar">
<button id="save-btn" class="primary">Save Layout</button>
<button id="load-btn" class="secondary">Load Layout</button>
<button id="clear-btn" class="secondary">Clear</button>
<span id="status"></span>
</div>
<div id="designer-container" class="container"></div>
<!-- Load SDK from your self-hosted location -->
<script src="https://seatsquirrel.yourdomain.com/sdk/designer-sdk.js"></script>
<script>
// Replace with your self-hosted URL
const SEATSQUIRREL_URL = 'https://seatsquirrel.yourdomain.com';
let currentLayoutId = null;
const designer = new SeatSquirrel.Designer({
container: '#designer-container',
baseUrl: SEATSQUIRREL_URL,
onReady: function () {
document.getElementById('status').textContent = 'Ready';
},
onExport: async function (layoutData) {
// Save to your backend
const response = await fetch('/api/layouts', {
method: currentLayoutId ? 'PUT' : 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: currentLayoutId,
data: layoutData,
}),
});
const result = await response.json();
currentLayoutId = result.id;
document.getElementById('status').textContent = 'Saved!';
setTimeout(() => {
document.getElementById('status').textContent = '';
}, 2000);
},
onLayoutChanged: function (data) {
if (data.hasChanges) {
document.getElementById('status').textContent = 'Unsaved changes';
}
},
onError: function (error) {
alert('Error: ' + error.message);
},
});
// Save button - triggers onExport callback
document.getElementById('save-btn').addEventListener('click', () => {
designer.getLayout();
});
// Load button
document.getElementById('load-btn').addEventListener('click', async () => {
const layoutId = prompt('Enter layout ID:');
if (!layoutId) return;
const response = await fetch(`/api/layouts/${layoutId}`);
const layoutData = await response.json();
designer.loadLayout(layoutData);
currentLayoutId = layoutId;
});
// Clear button
document.getElementById('clear-btn').addEventListener('click', () => {
if (confirm('Clear the current layout?')) {
designer.clearLayout();
currentLayoutId = null;
}
});
</script>
</body>
</html>SDK API Reference
The self-hosted SDKs have the same API as the hosted version. For complete documentation on all methods, callbacks, and data structures, see:
- Designer SDK API Reference - Full Designer API documentation
- Picker SDK API Reference - Full Picker API documentation
- SDK Overview - Quick reference for both SDKs
Key Callbacks
| SDK | Callback | Purpose |
|---|---|---|
| Designer | onExport(layoutData) | Receive layout data when user exports or getLayout() is called |
| Designer | onLayoutChanged(data) | Track unsaved changes |
| Picker | onSelectionChange(data) | Receive seat/area selections in real-time |
| Picker | onComplete(data) | Handle checkout when user clicks the checkout button |
Data Flow
- Designer: Create/edit layouts →
onExportcallback → Save to your database - Picker: Load layout from database →
loadLayout()→ User selects seats →onComplete→ Process checkout
Troubleshooting
Assets Not Loading
Problem: Designer/Picker page shows blank or broken
Solutions:
- Ensure all files from
dist/app/are deployed - Verify assets are served from the root path (
/assets/...) - Check browser console for 404 errors
- Ensure correct MIME types for
.jsand.cssfiles
"Access Denied" Error
Problem: Embedding fails with "Access Denied"
Solutions:
- Check
APPROVED_DOMAINSincludes your embedding domain - Include the full URL with protocol:
https://example.com - Include port for localhost:
http://localhost:3000 - Check browser console for specific error details
SDK Methods Not Working
Problem: Methods like loadLayout() don't work
Solutions:
- Wait for
onReadycallback before calling methods - Verify
baseUrlpoints to your self-hosted deployment - Check that the container element exists and has a height
- Check browser console for errors
Support
For issues specific to the self-hosted version, check:
- Browser console for JavaScript errors
- Network tab for failed requests
- Server logs for access/error information
For SDK API questions, refer to the Designer or Picker embedding documentation.