@williamcachamwri/markui-react
v0.2.0
Published
React runtime and built-in components for MarkUI
Maintainers
Readme
@williamcachamwri/markui-react
React renderer, isolated state/actions, two-way bindings and built-in components for versioned MarkUI documents.
Install
npm install @williamcachamwri/markui-core @williamcachamwri/markui-react react react-domSupported React versions are 18.3 and 19.
Render a document
import { compileMarkdown } from '@williamcachamwri/markui-compiler'
import { MarkUIProvider, MarkUIRenderer } from '@williamcachamwri/markui-react'
import '@williamcachamwri/markui-react/styles.css'
const { document } = compileMarkdown('# Hello {{ state.name }}')
export function App() {
return (
<MarkUIProvider state={{ name: 'Ada' }}>
<MarkUIRenderer document={document} />
</MarkUIProvider>
)
}MarkUIRenderer validates the document version before rendering.
Page logic
Markdown transformed by @williamcachamwri/markui-vite uses createMarkUIPage. A sidecar module can export setup:
export function setup() {
return {
state: { count: 0 },
actions: {
increment({ state }) {
state.count = Number(state.count) + 1
},
},
}
}Actions receive an isolated working copy. A successful action commits it; a thrown or rejected action restores the previous state. setState and getState are available for explicit updates.
Action transactions are serialized per page instance. Two asynchronous actions that finish out of order preserve both successful updates instead of replacing each other. Changing resetKey invalidates queued and running transactions from the previous logical page.
Route lifecycle
Use resetKey when the same page component renders another logical record:
<UserPage
resetKey={params.id}
params={{ id: params.id }}
/>When resetKey changes, setup runs again and page state/action status reset. Without a changed key, setup remains stable across ordinary renders.
Bindings and conditions
::input{name="email" bind="state.profile.email"}
:::alert{when="{{ state.saved }}" type="success"}
Saved.
:::Nested binding updates are immutable. Built-in input components accept DOM events and direct values.
Raw HTML
Raw HTML is rendered as text unless the application supplies a sanitizer:
<GeneratedPage
sanitizeHtml={(html) => trustedSanitizer(html)}
/>Only the returned sanitizer output is passed to dangerouslySetInnerHTML. The sanitizer must be maintained by the application and configured for its threat model.
Custom components
<GeneratedPage
components={{
badge: ({ children, tone }) => (
<span data-tone={String(tone)}>{children}</span>
),
}}
/>Component names must also be registered with the compiler or Vite plugin so invalid props can be diagnosed at build time.
Provider inputs
MarkUIProvider supports:
state,data,params,actionsandhelpers.componentsto override or extend built-ins.onErrorfor action errors.resetKeyfor logical page lifecycle resets.sanitizeHtmlfor explicitly trusted raw HTML rendering.
Built-in components
The default registry and renderer include button, card, grid, input, alert and stat.
Notable behavior:
buttonsupportsloadingLabeland exposesdata-action-status;gridclampscolumnsto 1–12 andgapto 0–24;inputuses ReactuseId()so labels remain unique across repeated pages;alertusesrole="alert"for errors and polite status semantics for other messages;statacceptslocaleandcurrencyinstead of assuming USD.
Styling without host-page side effects
Import component styling:
import '@williamcachamwri/markui-react/styles.css'The stylesheet scopes rules to .markui-* classes. Its only :root declarations are customizable --markui-* tokens, so it does not replace the host application font, background, or text color.
For a standalone MarkUI application, opt into the separate baseline:
import '@williamcachamwri/markui-react/base.css'
import '@williamcachamwri/markui-react/styles.css'Override tokens globally or under an application wrapper:
.my-product-theme {
--markui-color-primary: #5b3df5;
--markui-color-surface: #fff;
--markui-radius-md: 10px;
}The included markui-theme-dark class provides dark surface and text tokens without requiring a global color-scheme reset.
