INTEGRATIONS — REACT

Four Things Your React App Actually Needs to Do

Fetch content, take a form submission, answer a visitor's question, and respect their cookie choice. All four are plain HTTP calls except consent, which gets a real React hook. Every example on this page is copy-pasteable.

CONTENT LAYER

No SDK to Learn for Content

Some headless CMS platforms ship a client library you have to install, version, and learn a query syntax for. Zunoy's content layer is REST returning JSON, so it works with fetch, Axios, or React Query exactly as any other API would — nothing proprietary sitting between your components and your content.

SDK-dependent CMS

1

Learn a client library's query syntax and generated types

2

SDK version has to track platform releases

3

Locked into whatever the SDK's author chose to support

4

One SDK trying to cover every use case adequately

1

Plain fetch, or the data layer you already use

2

Nothing to version — it's an HTTP endpoint

3

The full REST API, every parameter available directly

4

One real React package for the one thing worth wrapping — consent

CONTENT

Fetch a Content Type in Four Lines

An API key and a GET request return typed JSON matching the fields you defined in the admin. No client library, no code generation step, and no defensive parsing — the schema is enforced before content is ever stored.

One fetch call per content type, standard REST semantics

Response fields match exactly what you defined in the content type

Works identically in a client component or a data-loading hook

Reading Content

Two ways to call the same endpoint — a raw fetch you can drop in anywhere, or a React Query hook if you already cache requests that way.

BlogList.jsx

function BlogList() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch("https://api.zunoy.cms/v1/post", {
      headers: { "X-Zunoy-Key": import.meta.env.VITE_ZUNOY_KEY }
    })
      .then((res) => res.json())
      .then((data) => setPosts(data.entries));
  }, []);

  return posts.map((post) => (
    <article key={post.id}>
      <h2>{post.title}</h2>
      <p>{post.excerpt}</p>
    </article>
  ));
}

FORMS

A Contact Form, Submitted With No Third-Party Tool

Every form lives on a branch, so the endpoint takes both in its path. Post JSON or let a plain HTML form submit itself — either way, the submission lands in the same admin as your content, with no separate form-tool account to create.

Branch-scoped endpoint

URL shape: POST /v1/forms/:branch/:slug

Two ways in

Accepts JSON from a controlled component, or a bare form post

Isolated per branch

Submissions are isolated per branch, never merged or shared

Submitting a Form

Post JSON from a controlled component, or skip JavaScript entirely and let the browser submit a plain HTML form straight to the endpoint.

ContactForm.jsx

function ContactForm() {
  const [status, setStatus] = useState("idle");

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus("sending");
    const data = Object.fromEntries(new FormData(e.target));

    const res = await fetch(
      "https://api.zunoy.cms/v1/forms/main/contact-us",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Zunoy-Key": import.meta.env.VITE_ZUNOY_KEY
        },
        body: JSON.stringify(data)
      }
    );

    setStatus(res.ok ? "sent" : "error");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button disabled={status === "sending"}>
        {status === "sending" ? "Sending…" : "Send"}
      </button>
    </form>
  );
}

AI SEARCH

A Search Bar That Answers, Not Just Links

The public ask endpoint returns a generated answer plus the source entries it drew from, so a search box can behave like a real answer rather than a results list. It runs entirely on published content — nothing you haven't shipped can be surfaced.

GET or POST /v1/_ask

Meters one AI credit per question

Cited sources

Every response includes cited sources, not just an answer

/v1/_search available

Raw semantic matches if you want them instead

See the AI Chatbot

Adding an Ask Bar

One hook wires a question in and a sourced answer out — no separate results-list UI to build.

AskBar.jsx

function AskBar() {
  const [question, setQuestion] = useState("");
  const [answer, setAnswer] = useState(null);

  async function ask(e) {
    e.preventDefault();
    const res = await fetch(
      `https://api.zunoy.cms/v1/_ask?q=${encodeURIComponent(question)}`,
      { headers: { "X-Zunoy-Key": import.meta.env.VITE_ZUNOY_KEY } }
    );
    setAnswer(await res.json());
  }

  return (
    <>
      <form onSubmit={ask}>
        <input
          value={question}
          onChange={(e) => setQuestion(e.target.value)}
          placeholder="Ask about our product…"
        />
      </form>
      {answer && (
        <div>
          <p>{answer.answer}</p>
          <ul>
            {answer.sources.map((s) => (
              <li key={s.id}><a href={s.url}>{s.title}</a></li>
            ))}
          </ul>
        </div>
      )}
    </>
  );
}

A Real React Hook, Not a Third Wrapper Around a Script Tag

ConsentProvider

Wraps your app once, at the root

useConsent()

Gives you needsConsent, isGranted, acceptAll, rejectAll

Region resolution

Region resolution and script gating are handled inside the package

Wiring Up Consent

Wrap the app once with the provider, then build whatever banner UI you want on top of the hook's state.

App.jsx

import { ConsentProvider } from "@zunoy/consent";

function App({ children }) {
  return (
    <ConsentProvider
      options={{
        baseUrl: "https://api.zunoy.cms/v1/cms",
        apiKey: import.meta.env.VITE_ZUNOY_KEY
      }}
    >
      {children}
    </ConsentProvider>
  );
}

AT A GLANCE

What You're Actually Wiring Up

Four integration points, four different shapes. Three are plain HTTP; one gets a real package because state management is worth not reinventing.

Content

GET requests, typed JSON, no SDK required.

Forms

POST to a branch-scoped endpoint, JSON or plain HTML.

AI Ask

A question in, an answer with sources out.

Consent

A provider and a hook — the one real package on this page.

Related Reading

Headless CMS API

The schema and endpoints your fetch calls hit

Form Builder

How submissions are stored, branched, and reviewed

AI Chatbot

How the ask endpoint grounds answers in your content

Cookie Consent

Region rules and vendor presets behind the consent hook

Common Questions

Do I need to install a Zunoy SDK to fetch content in React?

No. Content is served as plain REST JSON, so a standard fetch call or your existing data-fetching library works without installing anything Zunoy-specific.

Can I build a custom AI search or ask experience?

Yes. The public _ask endpoint returns a generated answer with cited sources, and _search returns raw semantic matches if you want to build your own results list instead of an answer format.

Does the consent package block scripts automatically?

Yes, when used with the vendor script helpers the package exports (injectVendorScript, syncVendorScripts). Configured vendor scripts stay out until the visitor grants that category.

How do I submit a form from React without a third-party form tool?

POST JSON or a browser-native form submission to /v1/forms/:branch/:slug with your API key. The submission is stored in the Zunoy admin alongside your content, no separate service required.

Is there a real package for cookie consent, or do I build it myself?

There's a real package, @zunoy/consent, with a ConsentProvider and a useConsent hook. It handles fetching your region-aware banner config and tracking grants — you render the banner markup yourself.

Are form submission webhooks or spam filtering available yet?

Not yet. Both are in active development. Submissions currently raise an in-app notification instead of a webhook, and abuse is bounded by a monthly quota rather than content-level spam filtering.

Wire Up All Four in an Afternoon

Content, forms, search, and consent — copy the examples above and swap in your own workspace key.

Ask a question about Zunoy's products, pricing, or docs.

⌘K