Slate is the preferred frontend deployment for all new Catalyst projects. It supersedes legacy Web Client Hosting with modern Git-based workflows and native framework support.
Key Features
- Native support: Next.js, React (Vite/CRA), Vue, Angular, Svelte, Astro, SolidJS, Preact, static HTML
- Git-based deployment (GitHub/GitLab repos)
- Auto build and deploy on push
- Preview deployments for branches
- Custom domain mapping
- Environment variables
- SSR and ISR support (Next.js)
CLI Commands
catalyst slate:create --name <name> --framework <framework> --default # Add Slate app
catalyst slate:link # ⚠️ Interactive — link existing dir
catalyst slate:unlink # Unlink a Slate app
catalyst serve --only slate # Serve locally
catalyst deploy slate # Deploy all Slate apps to Development
catalyst deploy slate -m "msg" # Deploy with message
catalyst deploy --only slate:name # Deploy specific app
catalyst deploy slate --production # Deploy to Production ⚠️Slate URL format (varies by data center):
| DC | URL format |
|---|---|
| US | https://<subdomain>.onslate.com |
| EU | https://<subdomain>.onslate.eu |
| IN | https://<subdomain>.onslate.in |
| AU | https://<subdomain>.onslate.au |
| CA | https://<subdomain>.onslate.ca |
Supported Frameworks
| Framework Value | Detection | Build Output |
|---|---|---|
static |
HTML/CSS/JS | . or public/ |
react-vite |
React + Vite | dist/ |
nextjs |
Next.js | out/ or .next/ |
vue |
Vue.js | dist/ |
angular |
Angular | dist/<project-name> |
svelte |
SvelteKit | dist/ or build/ |
astro |
Astro | dist/ |
solidjs |
SolidJS | dist/ |
preact |
Preact | dist/ |
create-react-app |
CRA | build/ |
Manual Setup (Non-Interactive)
catalyst slate:link is interactive-only. For automated environments:
Step 1 — Create .catalyst/slate-config.toml inside the build output directory:
framework = "static"
deployment_name = "default"No build step (pure static HTML/CSS/JS)? Your source directory is the output directory. Place
.catalyst/slate-config.tomldirectly inside yourclient/(or equivalent) folder. No build command needed.
Step 2 — Add to catalyst.json with absolute source path:
"slate": [{ "name": "my-frontend", "source": "/absolute/path/to/client" }]Step 3 — Deploy:
catalyst deploy slate -m "initial deploy"After deploying, load the hosted Slate URL and confirm it serves the fresh build — a successful deploy of the wrong
sourcedirectory silently re-serves the previous build's assets.
Local Development — Calling Functions from Slate
catalyst serve starts three servers on separate ports:
| What | URL |
|---|---|
| Function (API gateway) | http://localhost:3000/server/api/ |
| Slate (catalyst proxy) | http://localhost:3001 |
| Slate (Vite HMR — what you open in the browser) | http://localhost:4800 |
The silent failure: relative paths like /server/api/execute resolve against the Vite port (4800). Vite's SPA fallback returns index.html with status 200 — not a function response, not an error. Your fetch succeeds but you get HTML, not JSON.
Fix — use environment-specific base URLs:
# client/ui/.env.development
VITE_API_BASE=http://localhost:3000/server/api/execute
# client/ui/.env.production
VITE_API_BASE=https://<project>.development.catalystserverless.com/server/api/executeconst API = import.meta.env.VITE_API_BASE;
const res = await fetch(API, { credentials: 'include' });Port 3000 (the function) is stable. Port 4800 (Vite) may increment if already in use — never hardcode it. Always point
VITE_API_BASEat port 3000.
Common Errors
Deploy succeeds in CLI but app is not updated
catalyst deploy slate can exit with a success status even when the build failed on the server side. Always verify the deployment actually completed by checking: Console → Slate → your app → Deployments → confirm the latest entry shows Success and the build log has no errors.
slate-config.toml wiped by clean builds
The .catalyst/slate-config.toml lives inside the build output directory (e.g., dist/). Build commands that clean the output (--clean, rm -rf dist/) delete this file.
# Recreate after every clean build (Vite/React example):
npm run build && mkdir -p dist/.catalyst && \
echo -e 'framework = "static"\ndeployment_name = "default"' > dist/.catalyst/slate-config.toml && \
catalyst deploy slate
# Expo web example:
npx expo export --platform web --clear && \
mkdir -p dist/.catalyst && \
echo -e 'framework = "static"\ndeployment_name = "default"' > dist/.catalyst/slate-config.toml && \
catalyst deploy slatebaseUrl breaks assets on Slate
If your build config has a baseUrl or basePath set to a non-root path, all JS/CSS URLs get prefixed. Since Slate serves from root /, every asset returns 404.
Fix: Remove baseUrl/basePath from your build config before building for Slate. Only set it when serving from inside a function or AppSail sub-path.
Slate + Serverless Functions cross-origin (works with correct setup)
- Add Slate domain to Authorized Domains: Console → Authentication → Whitelisting → + Add Domain → enable CORS toggle
- Do NOT set CORS headers in function code for production origins — gateway injects them automatically
- Only set CORS headers for
localhost(local dev)
Slate + AppSail (cross-domain)
Slate (*.onslate.com) and AppSail (*.catalystappsail.com) run on separate domains. Register the Slate domain via MCP and remove any manual CORS headers from AppSail code — Catalyst injects them automatically. See catalyst-appsail/references/appsail-crossorigin.md for the full setup.
Slate vs Legacy Web Client Hosting
IMPORTANT: Slate and Web Client Hosting are different services with different behaviors. Understanding the difference is critical for authentication and routing.
| Aspect | Slate | Legacy Web Client Hosting |
|---|---|---|
| Serves from | Root / — *.onslate.com |
/app/ path — same domain as functions |
| Function call URLs | Must be absolute — https://<project>.catalystserverless.com/server/<fn>/execute |
Can be relative — /server/<fn>/execute |
| Routing controlled by | Framework router + _redirects |
client-package.json |
client-package.json role |
Optional, SDK hints only | Required, defines routing |
| SPA fallback | _redirects or .catalyst/slate-config.toml |
Automatic |
| Build output | Any (dist/, build/, out/) |
Must be client/ |
| Deployment command | catalyst deploy slate |
catalyst deploy (deploys client) |
| Environment variables | Build-time only (no runtime config) | Build-time only |
| Modern framework support | React, Next.js, Vue, Angular, Svelte, etc. | Basic HTML/CSS/JS |
⚠️ Migrating from legacy client hosting to Slate? Relative function URLs like
/server/fn/executethat worked before silently break on Slate — Slate is served from*.onslate.comwhile functions live on*.catalystserverless.com. Find and replace every relative/server/...path with its absolutehttps://<project>.catalystserverless.com/server/...equivalent, and addgenerateAuthToken()headers to each call.
client-package.json for Slate
The file is OPTIONAL for Slate. If included, the SDK reads login_redirect and homepage values to determine redirect behavior after authentication, but these do NOT control your app's actual routing.
Best practice for Slate + Embedded Auth:
{
"name": "your-app-name",
"version": "0.0.1",
"homepage": "/",
"login_redirect": "/"
}Place in public/ (Vite/CRA) or static/ (Next.js) so it's copied to build output. Paths MUST start with / to avoid legacy /app/ prefix.
SPA Routing (React, Vue, Angular)
Single-Page Applications use client-side routing. All paths must serve index.html to let the framework router handle navigation.
Method 1: _redirects File (Recommended)
Create public/_redirects (Vite/CRA) or static/_redirects (Next.js):
/* /index.html 200This file is automatically copied to build output and instructs Slate to:
- Serve
index.htmlfor ALL paths - Return 200 status (not 302 redirect)
- Let client-side router handle navigation
Method 2: slate-config.toml
For frameworks without public folder, add to .catalyst/slate-config.toml:
framework = "react-vite"
deployment_name = "default"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200Important: This file lives in build output (dist/.catalyst/slate-config.toml). Clean builds delete it. Recreate after each build or use _redirects method instead.
Framework-Specific Notes
Vite/React: Use public/_redirects (automatically copied to dist/)
Create React App: Use public/_redirects (automatically copied to build/)
Next.js Static: Not needed (Next.js handles fallback)
Angular: Use src/assets/_redirects (configure angular.json to copy to dist)
Vue: Use public/_redirects (automatically copied to dist/)
Testing SPA Routing
After deployment:
- Visit root URL → Should load
- Visit nested route (e.g.,
/marketplace/123) → Should load (not 404) - Refresh on nested route → Should stay on that route (not 404)
If step 2 or 3 fails, SPA fallback is not configured.
Environment Variables for Slate
Slate deployments are static builds. There is NO runtime environment variable configuration in the Console. All environment variables must be set at build time.
Vite / React (CRA) / Vue
- Create
.env.productionin project root:
VITE_API_BASE=https://your-function.catalystserverless.in/server/your_api
VITE_CATALYST_ZAID=your_production_zaid- Build:
npm run build- Deploy:
catalyst deploy slateVariables are bundled into the static assets and cannot be changed without rebuilding.
Next.js (SSR/ISR)
Next.js on Slate supports runtime variables via NEXT_PUBLIC_* prefix:
// next.config.js
module.exports = {
env: {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
}
}Set during build:
NEXT_PUBLIC_API_URL=https://... npm run build
catalyst deploy slateAngular
Use environment.prod.ts:
export const environment = {
production: true,
apiUrl: 'https://your-function.catalystserverless.in/server/api'
};Build: ng build --configuration production
Key Principle
Build-time only. Changing variables requires rebuilding and redeploying. This is different from serverless functions which support runtime environment variables in the Console.
Web SDK Setup (Required for Auth)
Two scripts are required, in this exact order:
<!-- 1. Main Catalyst CDN bundle — MUST come first -->
<script src="https://static.zohocdn.com/catalyst/sdk/js/4.6.1/catalystWebSDK.js"></script>
<!-- 2. Project-specific init -->
<script src="/__catalyst/sdk/init.js"></script>Without catalystWebSDK.js, init.js crashes immediately and window.catalyst is never set — auth calls fail silently.
Slate + Embedded Auth: Legacy /app/ Path Handling
CRITICAL: When using Embedded Authentication with Slate, the Catalyst SDK may redirect to /app/ (legacy Web Client Hosting path). Since Slate serves from root /, this causes 404 errors.
Symptom
- User accesses Slate URL
- Redirected to
https://your-app.onslate.com/app/ - Returns 404 or "PATTERN_NOT_MATCHED" error
Root Cause
The /__catalyst/sdk/init.js script contains legacy redirect logic from Web Client Hosting that appends /app/ when:
login_redirectdoesn't start with/inclient-package.json, OR- Certain Console authentication configurations are set
Legacy Web Client Hosting served apps from /app/ path. Slate serves from /.
Solution (React Router / SPA frameworks)
Add catch-all routes that redirect /app/* back to root:
// React Router v6
import { Navigate } from "react-router-dom";
<Routes>
{/* ... other routes ... */}
{/* Legacy /app/ redirect for Catalyst SDK compatibility */}
<Route path="/app" element={<Navigate to="/" replace />} />
<Route path="/app/*" element={<Navigate to="/" replace />} />
<Route path="*" element={<NotFound />} />
</Routes>// Vue Router
{
path: '/app/:pathMatch(.*)*',
redirect: '/'
}// Angular
{
path: 'app',
redirectTo: '/',
pathMatch: 'full'
},
{
path: 'app/**',
redirectTo: '/'
}Solution (Static HTML / No Router)
Add to public/_redirects:
/app/* / 200Prevention
Ensure client-package.json paths start with /:
{
"homepage": "/",
"login_redirect": "/"
}Note: For Slate apps, client-package.json is read by the SDK but does NOT control actual routing (unlike legacy hosting). It only influences SDK redirect behavior.