Appearance
Static Website
Static Website is a service for hosting static web applications on the Tailor Platform.
Overview
Static Website provides:
- Static file hosting
- Type-safe URL references for configuration
- IP address restrictions
- Custom domain support
For the official Tailor Platform documentation, see Static Website Guide.
Configuration
Configure static website hosting using defineStaticWebSite():
Definition Rules:
- Multiple websites allowed: You can define multiple static websites in your config file
- Configuration location: Define in
tailor.config.tsand add to thestaticWebsitesarray - Uniqueness: Website names must be unique across all static websites
typescript
import { defineStaticWebSite, defineConfig } from "@tailor-platform/sdk";
const website = defineStaticWebSite("my-website", {
description: "My Static Website",
});
// You can define multiple static websites
const adminWebsite = defineStaticWebSite("admin-website", {
description: "Admin Dashboard",
});
export default defineConfig({
staticWebsites: [website, adminWebsite], // Add all websites to the array
});Options
description
A description of the static website:
typescript
defineStaticWebSite("my-website", {
description: "Frontend application for my service",
});allowedIpAddresses
Restrict access to specific IP addresses in CIDR format:
typescript
defineStaticWebSite("my-website", {
allowedIpAddresses: ["192.168.0.0/24", "10.0.0.0/8"],
});customDomains
Associate custom domains with the static website:
typescript
defineStaticWebSite("my-website", {
customDomains: ["app.example.com"],
});After deploying, use tailor staticwebsite domain get <domain> to check domain status and retrieve the CNAME targets required for DNS configuration.
A domain can be associated with only one workspace at a time. To set custom domains only in the workspace that owns the domain, see Multi-Environment Configuration. To hand a domain to another site or workspace without taking it offline, see Moving a domain.
Moving a domain
A domain already serving traffic can be moved to another static website — in this workspace or another one — without downtime, without reissuing its certificate and without changing DNS. The site that currently holds the domain offers it; the receiving site accepts by listing the domain normally.
On the side giving the domain up, replace the plain entry with the destination:
typescript
defineStaticWebSite("my-website", {
customDomains: [{ domain: "app.example.com", transferTo: { workspaceId: "..." } }],
});Deploying opens the offer. On the receiving side, list the domain as usual and deploy:
typescript
defineStaticWebSite("new-website", {
customDomains: ["app.example.com"],
});That deploy completes the move. The next plan on the giving side reports that the domain moved, and the entry should then be deleted — it describes a move that already happened, not ongoing state.
The two deploys must be separate runs, even when both sites live in the same workspace: the receiving side cannot accept an offer that the same run is still opening, and tailor deploy says so rather than failing partway.
These rules apply while an offer is open:
- An offer expires after 7 days. Deploying the entry again after that opens a fresh offer.
- Neither the domain nor the static website holding it can be removed. Change the entry back to a plain string and deploy to withdraw the offer first;
tailor deployandtailor removeboth refuse before changing anything. - An offer takes a moment to become ready. Until it is, the receiving side cannot accept it and its deploy is refused; the giving side's plan says so.
Type-safe URL References
The returned website object provides a url property that resolves to the actual URL at deployment time. Use this for type-safe configuration:
CORS Settings
typescript
const website = defineStaticWebSite("my-frontend", {
description: "Frontend application",
});
export default defineConfig({
cors: [website.url], // Resolved at deployment
staticWebsites: [website],
});OAuth2 Redirect URIs
typescript
const website = defineStaticWebSite("my-frontend", {
description: "Frontend application",
});
const auth = defineAuth("my-auth", {
oauth2Clients: {
"my-client": {
redirectURIs: [
`${website.url}/callback`, // https://my-frontend.example.com/callback
`${website.url}/auth/complete`, // https://my-frontend.example.com/auth/complete
],
grantTypes: ["authorization_code", "refresh_token"],
},
},
});Application Environment Variables
typescript
const website = defineStaticWebSite("my-frontend", {
description: "Frontend application",
});
export default defineConfig({
env: {
siteUrl: website.url, // https://my-frontend.example.com
},
staticWebsites: [website],
});Resolver, executor, workflow job, and auth before-login hook code, and TailorDB migration scripts, that read env receive the deployed URL, even when the same deploy both creates the website and reads its URL — one deploy call resolves it, with no second, manually-triggered deploy needed. If the referenced website does not exist at all, the CLI warns and leaves the unresolved reference in place. If the reference still can't be resolved after this deploy's rebuild, the deploy fails instead of shipping the unresolved reference. This platform lookup only happens during deploy; function run passes the literal <name>:url string unchanged, since it never talks to the platform to resolve it.
The deployed URL is also shown by tailor show, which lists the URL of each static website defined in staticWebsites once it has been deployed.
Complete Example
typescript
import { defineConfig, defineAuth, defineIdp, defineStaticWebSite } from "@tailor-platform/sdk";
import { user } from "./tailordb/user";
const website = defineStaticWebSite("my-frontend", {
description: "Frontend application",
});
const idp = defineIdp("my-idp", {
clients: ["default-client"],
permission: {
create: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
read: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
update: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
delete: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
sendPasswordResetEmail: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
},
});
const auth = defineAuth("my-auth", {
userProfile: {
type: user,
usernameField: "email",
attributes: { role: true },
},
oauth2Clients: {
"frontend-client": {
redirectURIs: [`${website.url}/callback`],
grantTypes: ["authorization_code", "refresh_token"],
},
},
idProvider: idp.provider("default", "default-client"),
});
export default defineConfig({
name: "my-app",
cors: [website.url],
idp: [idp],
auth,
staticWebsites: [website],
});