@nascencestudio/tapestry
v0.4.0
Published
Drag-and-drop component page building for Astro + StudioCMS
Maintainers
Readme
@nascencestudio/tapestry
Drag-and-drop, component-based page building for Astro + StudioCMS.
Register your own Astro components with a typed prop schema; editors compose pages from them in the StudioCMS dashboard on a live canvas of the real page. Pages are stored as validated JSON and rendered on the server as your components, with no client JavaScript unless a component opts in.
- Visual canvas with drag and drop, inline text editing, keyboard navigation
- Drafts, publishing, scheduled publishing, version history and comparison
- Rich text (ProseMirror, per-field toolbars), links, media (with
@nascencestudio/medialibrary), lists and objects, named slots - Patterns (saved sections), translations, component permissions, migrations
- Interactive components (Astro islands) when a component opts in
- SEO with
@nascencestudio/tapestry-seo: an SEO button in the editor for each page's search title, description and social image, robots.txt and a sitemap
Inspired by Drupal Canvas. The editor is built with Preact (about 35 KB gzipped on first load, dashboard only); page content is treated as untrusted input: strict schema validation, size and depth limits, URL scheme allowlists and an element allowlist on the rendered output (security).
Requirements
- Node ≥ 22.12, Astro 7, StudioCMS 0.6
- An on-demand (SSR) Astro adapter, as StudioCMS requires
Install
pnpm add @nascencestudio/tapestrySet up
1. Define components (any .astro file can be one):
// src/tapestry.config.mjs
import { defineComponent } from "@nascencestudio/tapestry";
export const components = [
defineComponent({
type: "hero",
label: "Hero",
component: "./src/components/Hero.astro",
props: {
heading: { type: "text", label: "Heading", required: true },
link: { type: "link", label: "Button link" },
},
}),
];2. Register the plugin and Tapestry's component registry entries (StudioCMS plugins can't add those themselves):
// studiocms.config.mjs
import tapestry, { tapestryComponentRegistry } from "@nascencestudio/tapestry";
import { defineStudioCMSConfig } from "studiocms/config";
import { components } from "./src/tapestry.config.mjs";
export default defineStudioCMSConfig({
componentRegistry: { ...tapestryComponentRegistry() },
plugins: [tapestry({ components })],
});3. Load pages with getPage() in your page route. It enforces draft
visibility (StudioCMS's own bySlug returns drafts) and picks the published
version for visitors:
---
// src/pages/[...slug].astro
import { StudioCMSRenderer } from 'studiocms:renderer';
import { getPage } from '@nascencestudio/tapestry/page';
import AdminBar from '@nascencestudio/tapestry/AdminBar.astro';
const result = await getPage(Astro);
if (!result) return new Response(null, { status: 404 });
const { page, viewer, publishing } = result;
---
<html lang="en">
<body>
<AdminBar viewer={viewer} page={page} publishing={publishing} />
<StudioCMSRenderer data={page} />
</body>
</html>Then create a page of type Tapestry (visual builder) in the dashboard and build it on its Page Content tab.
Optional companions, picked up automatically when installed and registered as StudioCMS plugins:
@nascencestudio/medialibrary (media props, images
in rich text; option mediaLibrary) and
@nascencestudio/tapestry-seo (the editor's SEO
button; option seo).
Interactive components need a StudioCMS patch
StudioCMS 0.6.1 removes every <script> that components render from page
content, so islands (client: 'visible' etc.) never become interactive and
<script>s in your components don't run. Until StudioCMS changes this, apply the
small patch that ships with this package (it only affects page types that opt in,
like Tapestry):
mkdir -p patches
cp node_modules/@nascencestudio/tapestry/patches/[email protected] patches/# pnpm-workspace.yaml
patchedDependencies:
[email protected]: patches/[email protected]Then pnpm install. (With npm, use patch-package
with the same file.) Sites without interactive components don't need it.
Documentation
- Defining components: prop types, slots, lists, links, media, islands, migrations, permissions
- Site integration:
getPage(), the admin bar, translations, 404 page - Security, the data model and the architecture
- Known issues, the roadmap and the design decisions
Development
Requires Node ≥ 22.12 and pnpm ≥ 12 (pnpm only; the supply-chain policies in
pnpm-workspace.yaml apply to every install).
pnpm install
pnpm build # dist/
pnpm test # unit tests (Vitest)
pnpm typecheck && pnpm lintThe dev site, browser tests and deployment setup live in nascencestudio/tapestry-playground. See Contributing and development.
How Tapestry is built
Tapestry is developed with an AI coding assistant (Anthropic's Claude), directed by its
maintainer at Nascence Studio, who sets the requirements, makes the decisions and checks the
results. Commits written with its help say so (Co-Authored-By).
What keeps that accountable:
- Decisions are written down. Every significant choice has a decision record in docs/decisions, with the alternatives considered and why they lost. A devlog records each working session, mistakes included.
- Behaviour is tested. Unit tests cover the plugin's logic, with adversarial cases for everything that handles untrusted input (validation, URLs, rich text). Browser tests drive the real editor in Chrome, including drag and drop, publishing and an accessibility audit in both dashboard themes; they live in tapestry-playground.
- Security is designed in. Page content is treated as untrusted input from start to finish, and the threat model is in docs/security.md. Dependencies are minimal, pinned and reviewed before they're added; pnpm's supply-chain checks run on every install.
- Releases are traceable. Versions are published from GitHub Actions with npm provenance, and each one is approved by a person with two-factor authentication.
Found a problem? Please open an issue.
NOTE: This is mostly an expirement of co-authoring something with AI. If you find it useful, awesome.
License
MIT © Nascence Studio
