openElement has three layers: JSX-first application APIs, a DSD renderer, and progressive island upgrades.
App authors start from @openelement/app. The API produces Web Platform output without making every page author extend a base class.
import { definePage } from '@openelement/app';
export default definePage({
route: { path: '/' },
head: { title: 'Home' },
render() {
return <main>Hello openElement</main>;
},
});The object form keeps route data, document metadata, and rendering intent together. Layout composition stays in app shell and renderer configuration.
import { definePage } from '@openelement/app';
export default definePage({
route: { path: '/posts/[slug]', params: ['slug'] },
head: {
title: 'Post',
description: 'A static post page',
},
renderIntent: { mode: 'static' },
async load({ params }) {
return { slug: params.slug };
},
render({ data }) {
return <article>{data.slug}</article>;
},
});Use defineElement() for reusable DSD components anddefineIsland() for browser-upgraded interactivity.
import { defineElement, defineIsland } from '@openelement/app';
import { signal } from '@openelement/runtime';
export const Badge = defineElement('app-badge', ({ label }) => {
return <span part='badge'>{label}</span>;
});
const count = signal(0);
export default defineIsland('my-counter', () => (
<button onClick={() => count.value++}>
Count: {count.value}
</button>
));DsdElement, renderDsd(), JSX runtime helpers, signals, and StyleSheet remain the runtime primitives. They are still public, but they are no longer the first thing a page author needs to write.
There is one renderer model:
JSX -> VNode -> RenderNode -> DSD HTML or DOM