
Security News
vlt 1.5.0 Adds Agent Skills Management
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.
@mapled/react
Advanced tools
React helpers for Mapled — published content in a React single-page app (Vite and the like): a provider and hooks over @mapled/client, with preview and publish notifications.
React helpers for Mapled, the hosted headless CMS for sites built with AI: published content in a React single-page app — Vite, Create React App, anything that renders in the browser. One provider, three hooks. Built on @mapled/client; a Next.js site uses @mapled/next instead.
npm install @mapled/react
@mapled/client comes with it. Put the project's delivery key (Mapled: Integrations → Your site) in .env:
VITE_MAPLED_KEY=mk_live_…
It is bundled into the page, which is fine for this key and for no other: it reads published content only. A server key or a webhook signing secret never goes under a VITE_ name.
// src/mapled.ts
import { createClient } from "@mapled/client";
import { typedHooks } from "@mapled/react";
import type { MapledSchema } from "./mapled-types";
export const mapled = createClient<MapledSchema>({ key: import.meta.env.VITE_MAPLED_KEY });
export const { useSingle, useRecords, useRecord } = typedHooks<MapledSchema>();
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { MapledProvider } from "@mapled/react";
import { mapled } from "./mapled";
import { App } from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<MapledProvider client={mapled}>
<App />
</MapledProvider>
</StrictMode>,
);
mapled-types.ts is written by npx @mapled/cli types generate; with typedHooks<MapledSchema>() keys are checked and data has each collection's own type. Without types, import useSingle, useRecords and useRecord straight from @mapled/react.
Every hook answers { data, loading, error, refetch }.
// src/pages/Home.tsx
import Markdown from "react-markdown";
import { assetUrl } from "@mapled/client";
import { useSingle } from "../mapled";
export function Home() {
const { data: home, loading, error } = useSingle("homepage");
if (loading) return <p>Loading…</p>;
if (error || !home) return <p>Nothing here yet.</p>;
return (
<main>
<h1>{home.data.headline}</h1>
{home.data.hero ? <img src={assetUrl(home.data.hero, { width: 1200 })} alt="" /> : null}
<Markdown>{home.data.intro ?? ""}</Markdown>
</main>
);
}
// src/pages/Blog.tsx
import { Link } from "react-router-dom";
import { useRecords } from "../mapled";
export function Blog() {
const { data, loading } = useRecords("posts", { sort: "-date", limit: 20, fields: ["title", "slug", "date"] });
if (loading && !data) return <p>Loading…</p>;
return (
<ul>
{data?.records.map((post) => (
<li key={post.id}>
<Link to={`/blog/${post.data.slug}`}>{post.data.title}</Link>
</li>
))}
</ul>
);
}
// src/pages/Post.tsx
import Markdown from "react-markdown";
import { useParams } from "react-router-dom";
import { useRecord } from "../mapled";
export function Post() {
const { slug } = useParams();
const { data: post, loading } = useRecord("posts", slug ? { slug } : null);
if (loading) return <p>Loading…</p>;
if (!post) return <p>This post doesn't exist.</p>;
return (
<article>
<h1>{post.data.title}</h1>
<Markdown>{post.data.body}</Markdown>
</article>
);
}
useSingle(key, options?) — data is the single's record, or null while it is empty.useRecords(collection, options?) — data is { records, total, release }; options are the client's: filter, sort, limit, offset, fields, expand, locale (read in one of the project's languages; createClient({ key, locale }) for every read).useRecord(collection, { slug } | { id } | null, options?) — data is null when the record isn't published; null instead of a reference reads nothing (a router param that hasn't arrived).useMapled() hands out the client for a read the hooks don't cover.Nothing to add: when someone opens Preview in Mapled, the page opens with a one-time token in its address, the client trades it for a draft session, and the hooks show drafts in that tab. Say so on the page:
// src/PreviewBar.tsx
import { usePreview } from "@mapled/react";
export function PreviewBar() {
const preview = usePreview();
if (!preview.active) return null;
return (
<p role="status">
You are looking at a preview. <button onClick={preview.exit}>Back to the published site</button>
</p>
);
}
Mapled has to know where the site lives and that it renders in the browser: the site manifest's framework is "react-spa" (npx @mapled/cli scan --write, bindings push — or your AI agent's push_site_manifest), and Production domain in Project settings holds the site's public address (an agent sets it with set_site_url). There is no webhook to configure and no route to mount.
A publish shows when the page is loaded again. To follow it while the page is open:
// src/LiveApp.tsx
import { MapledProvider } from "@mapled/react";
import { mapled } from "./mapled";
import { App } from "./App";
export function LiveApp() {
return (
<MapledProvider client={mapled} live>
<App />
</MapledProvider>
);
}
Each open tab then asks Mapled for the release pointer once a minute (live={{ interval: 30 }} for thirty seconds, never under fifteen) while it is visible, and the hooks read again — pinned to the new release — when it moves; what was on screen stays there until the new answer arrives. The watch starts from the release the hooks' reads answered, so a publish that landed between them and the first check, or while a tab opened in the background waited to be shown, is heard at once. It is off by default because every check is a request of the delivery key's budget (1,200 a minute).
submitForm from @mapled/client posts a visitor's submission:
// src/ContactForm.tsx
import { useState, type FormEvent } from "react";
import { submitForm } from "@mapled/client";
const CONSENT = "I agree to be contacted about my request.";
export function ContactForm() {
const [state, setState] = useState<"idle" | "sending" | "sent" | "failed">("idle");
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const form = event.currentTarget;
setState("sending");
const answer = await submitForm("contact", form, {
key: import.meta.env.VITE_MAPLED_KEY,
consent: new FormData(form).get("consent") ? CONSENT : undefined,
});
setState(answer.ok ? "sent" : "failed");
}
if (state === "sent") return <p>Thank you — we'll be in touch.</p>;
return (
<form onSubmit={submit}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<input name="_gotcha" tabIndex={-1} autoComplete="off" aria-hidden="true" style={{ display: "none" }} />
<label>
<input name="consent" type="checkbox" required /> {CONSENT}
</label>
<button disabled={state === "sending"}>Send</button>
{state === "failed" ? <p>That didn't go through. Try again in a minute.</p> : null}
</form>
);
}
npx @mapled/cli doctor
doctor knows a single-page app: it wants VITE_MAPLED_KEY and no webhook secret, reads ○ «Not needed» for the webhook and the routes, and fails an env file that puts a server key or a signing secret under a public prefix.
| Export | |
|---|---|
MapledProvider | client, live? (true or { interval }) |
useSingle, useRecords, useRecord | { data, loading, error, refetch } |
typedHooks<MapledSchema>() | The same three hooks, typed by the generated schema |
usePreview() | { active, exit } |
useMapled() | The provider's client |
FAQs
React helpers for Mapled — published content in a React single-page app (Vite and the like): a provider and hooks over @mapled/client, with preview and publish notifications.
The npm package @mapled/react receives a total of 21 weekly downloads. As such, @mapled/react popularity was classified as not popular.
We found that @mapled/react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.

Security News
Rust 1.100 will add opt-in dependency cooldowns to Cargo, giving security scanners and maintainers time to catch malicious crate releases.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.