@antlur/backstage
v1.12.16
Published
A simple client for Backstage CMS
Maintainers
Readme
@antlur/backstage
A TypeScript client library for Backstage CMS with type-safe block and layout definitions.
Features
- 🔐 Type-safe API client for Backstage CMS
- 🧱 Define custom blocks with full TypeScript support
- 📐 Define custom layouts with schema validation
- � Define custom blueprints (content types) with field schemas
- 🔄 CLI tools for syncing blocks, blueprints, and layouts
- ⚛️ React components for page metadata and structured data
- 🎨 Framework-agnostic design (works with Next.js, React, etc.)
Installation
npm install @antlur/backstage
# or
yarn add @antlur/backstage
# or
pnpm add @antlur/backstageQuick Start
1. Environment Setup
Create a .env file in your project root:
# Required
BACKSTAGE_API_KEY=your_api_key_here
BACKSTAGE_ACCOUNT_ID=your_account_id_here
# Optional (defaults to https://bckstg.app/api)
BACKSTAGE_API_URL=https://bckstg.app/api2. Configure Backstage
Create a backstage.config.ts file:
import { defineConfig } from "@antlur/backstage";
export default defineConfig({
accountId: process.env.BACKSTAGE_ACCOUNT_ID,
token: process.env.BACKSTAGE_API_KEY,
// Optional: define your blocks and layouts here
blocks: [],
layouts: [],
});3. Use the Client
import { BackstageClient } from "@antlur/backstage";
const client = new BackstageClient();
// Fetch pages
const pages = await client.pages.getPages();
const homePage = await client.pages.getHomePage();
const page = await client.pages.getPageBySlug("about");
// Fetch locations
const locations = await client.locations.getLocations();
const location = await client.locations.getLocationBySlug("downtown");
// Fetch events
const events = await client.events.getEvents();
const event = await client.events.getEventBySlug("summer-festival");
// Fetch menus
const menu = await client.menus.getMenu("main-menu");Defining Custom Blocks
Create type-safe blocks for your CMS:
// blocks/hero/schema.ts
import { defineBlockSchema, defineField } from "@antlur/backstage/studio";
const heroFields = [
defineField({
name: "Title",
slug: "title",
type: "text",
required: true,
placeholder: "Enter hero title...",
}),
defineField({
name: "Subtitle",
slug: "subtitle",
type: "textarea",
description: "Optional subtitle text",
}),
defineField({
name: "Background Image",
slug: "backgroundImage",
type: "image",
required: true,
}),
defineField({
name: "Display Style",
slug: "displayStyle",
type: "select",
options: [
{ label: "Full Width", value: "full" },
{ label: "Centered", value: "centered" },
{ label: "Left Aligned", value: "left" },
],
required: true,
}),
defineField({
name: "Featured Products",
slug: "featuredProducts",
type: "reference",
allowed_references: ["products"],
is_multiple: true,
description: "Select products to feature",
}),
] as const;
export const schema = defineBlockSchema({
fields: heroFields,
});
export default schema;// blocks/hero/component.tsx
import type { BlockComponentProps } from "@antlur/backstage/studio";
import schema from "./schema";
export default function Hero({ block }: BlockComponentProps<typeof schema>) {
const { title, subtitle, backgroundImage } = block.fields;
return (
<div className="hero" style={{ backgroundImage: `url(${backgroundImage.url})` }}>
<h1>{title}</h1>
{subtitle && <p>{subtitle}</p>}
</div>
);
}// blocks/hero/index.ts
import { defineBlock } from "@antlur/backstage/studio";
import schema from "./schema";
import Hero from "./component";
export const heroBlock = defineBlock({
name: "Hero",
slug: "hero",
description: "A hero section with title, subtitle, and background image",
schema,
component: Hero,
});Defining Custom Blueprints
Create content types (blueprints) with custom field schemas:
// blueprints/customer.ts
import { defineBlueprint } from "@antlur/backstage/studio";
export const customerBlueprint = defineBlueprint({
name: "Customer",
slug: "customers",
description: "Customer testimonials and information",
fields: [
{
name: "Name",
slug: "name",
type: "text",
is_primary: true, // Primary field for display
required: true,
placeholder: "Customer full name",
},
{
name: "Email",
slug: "email",
type: "email",
required: true,
placeholder: "[email protected]",
},
{
name: "Company",
slug: "company",
type: "text",
placeholder: "Company name",
},
{
name: "Photo",
slug: "photo",
type: "image",
},
{
name: "Testimonial",
slug: "testimonial",
type: "textarea",
required: true,
placeholder: "Customer testimonial...",
},
{
name: "Rating",
slug: "rating",
type: "select",
options: [
{ label: "5 Stars", value: 5 },
{ label: "4 Stars", value: 4 },
{ label: "3 Stars", value: 3 },
{ label: "2 Stars", value: 2 },
{ label: "1 Star", value: 1 },
],
required: true,
},
{
name: "Show in Testimonials",
slug: "showInTestimonials",
type: "boolean",
show_in_list: true, // Show in admin list view
},
],
});Add blueprints to your config:
import { defineConfig } from "@antlur/backstage";
import { customerBlueprint } from "./blueprints/customer";
export default defineConfig({
accountId: process.env.BACKSTAGE_ACCOUNT_ID,
token: process.env.BACKSTAGE_API_KEY,
blueprints: [customerBlueprint],
});Using Reference Fields
Reference fields allow you to link to entries from specific blueprints. You can constrain which blueprints are allowed:
// blocks/testimonials/schema.ts
import { defineBlockSchema, defineField } from "@antlur/backstage/studio";
const testimonialFields = [
defineField({
name: "Customers",
slug: "customers",
type: "reference",
description: "Select customer entries",
allowed_references: ["customers"], // Only allow entries from the "customers" blueprint
is_multiple: true, // Allow multiple customer selections
required: true,
}),
defineField({
name: "Quote",
slug: "quote",
type: "textarea",
description: "The testimonial quote",
required: true,
}),
];
export const schema = defineBlockSchema({
fields: testimonialFields,
});In your component, the reference field will contain the full entry data:
// blocks/testimonials/component.tsx
import type { BlockComponentProps } from "@antlur/backstage/studio";
import schema from "./schema";
export default function Testimonials({ block }: BlockComponentProps<typeof schema>) {
const { customers, quote } = block.fields;
return (
<div className="testimonial">
<blockquote>"{quote}"</blockquote>
<cite>
- {customers.map(customer => customer.name).join(", ")}
</cite> {/* Access entry properties */}
</div>
);
}Field Options
When defining fields for blocks and blueprints, you can set various options to customize their behavior:
Common Field Options
name(required): Display name for the fieldslug(required): Unique identifier for the fieldtype(required): Field type (see supported types below)description: Help text shown to content editorsplaceholder: Placeholder text shown in input fieldsrequired: Whether the field is requiredoptions: For select fields, array of{ label: string, value: any }optionsallowed_references: For reference fields, array of blueprint slugs to referenceis_multiple: For reference fields, allow multiple selections
Blueprint-Specific Options
is_primary: Mark as the primary field (used for display in lists)show_in_list: Show this field in admin list viewsorder: Display order in forms (number)type_id: For fieldset references, the ID of the fieldset
CLI Commands
Sync your blocks, blueprints, and layouts to Backstage CMS:
# Sync blocks only
npx backstage sync blocks
# Sync blueprints only
npx backstage sync blueprints
# Sync layouts only
npx backstage sync layouts
# Sync everything
npx backstage sync allAdd this to your backstage.config.ts:
import { defineConfig } from "@antlur/backstage";
import { heroBlock } from "./blocks/hero";
export default defineConfig({
accountId: process.env.BACKSTAGE_ACCOUNT_ID,
token: process.env.BACKSTAGE_API_KEY,
blocks: [heroBlock],
});Available Services
The client provides the following services:
client.pages- Page managementclient.blocks- Block managementclient.blueprints- Blueprint (content type) managementclient.layouts- Layout managementclient.locations- Location managementclient.events- Event managementclient.menus- Menu managementclient.navigation- Navigation managementclient.media- Media managementclient.alerts- Alert managementclient.press- Press release managementclient.website- Website settingsclient.instagram- Instagram integration
Field Types
Supported field types for blocks, blueprints, and layouts:
text- Single-line text inputtextarea- Multi-line text inputrich_text- Rich text editormarkdown- Markdown editornumber- Numeric inputboolean- Checkboxselect- Select from predefined optionsreference- Reference to entries from specific blueprints (supportsis_multiplefor multiple selections)url- URL inputemail- Email inputslug- URL slug inputdate- Date pickertime- Time pickerdatetime- Date and time pickerlocation- Location pickerimage- Single image pickerimage_list- Multiple image pickermedia- Media item pickerlist_array- Array of stringsrepeater- Repeatable field groupfieldset- Grouped fieldsevent_select- Event selectormenu_select- Menu selectorform_select- Form selectorpress_select- Press release selectornavigation_select- Navigation selectorpage_select- Page selector
React Components
import { PageMeta } from "@antlur/backstage";
export default function MyPage({ page }) {
return (
<>
<PageMeta page={page} />
{/* Your page content */}
</>
);
}TypeScript Support
This library is written in TypeScript and provides full type definitions. All API responses are typed, and block/layout definitions provide excellent autocompletion and type safety.
License
MIT
Contributing
Issues and pull requests are welcome! Please visit the GitHub repository.
Support
For questions or support, please open an issue on GitHub or contact the Backstage CMS team.
