--- description: One System content rules — all editable website content lives in public/onesystem-content.json globs: alwaysApply: true --- # One System content — rules for this website (keep this section) This website's editable content is managed by One System (https://copilot.onesystemcrm.com) so the business owner can change it by chat, with a preview and Undo. Follow these rules every time you build or change a page. ## 1. Editable content lives in `public/onesystem-content.json`, never typed into components Everything a business owner might want to change goes in that file: - headings, paragraphs, button labels, prices, opening hours, offers - phone, email, address, WhatsApp/booking/social links - images people may swap (and a description for each image) - each page's Google title and description Layout, styles, class names, components and logic stay in code. Only content goes in the file. ## 2. The file's shape ```json { "version": 1, "pages": [ { "path": "/", "name": "Home" }, { "path": "/about", "name": "About" } ], "fields": { "home.hero.title": { "label": "Home — main heading", "type": "text", "page": "/", "value": "Fresh bread every morning" }, "home.hero.text": { "label": "Home — text under the heading", "type": "paragraph", "page": "/", "value": "Baked in Austin since 1998." }, "home.hero.button": { "label": "Home — button", "type": "text", "page": "/", "value": "See our menu" }, "home.hero.button_link": { "label": "Home — button link", "type": "link", "page": "/", "value": "/menu" }, "home.hero.image": { "label": "Home — main photo", "type": "image", "page": "/", "value": "/images/hero.jpg" }, "home.hero.image_alt": { "label": "Home — main photo description", "type": "text", "page": "/", "value": "Loaves on the counter" }, "contact.phone": { "label": "Phone number", "type": "phone", "page": "*", "value": "+1 512 555 0142" }, "contact.email": { "label": "Email", "type": "email", "page": "*", "value": "hello@example.com" }, "seo./.title": { "label": "Home — Google title", "type": "seo_title", "page": "/", "value": "Fresh Bread in Austin | Example Bakery" }, "seo./.description": { "label": "Home — Google description", "type": "seo_description", "page": "/", "value": "Sourdough, pastries and coffee, baked fresh every morning." } } } ``` - **Keys:** short and stable, with dots for sections (`home.hero.title`, `about.team.1.name`, `contact.phone`). Google title and description use `seo..title` and `seo..description`, for example `seo./about.title`. Never rename a key once the site is live: a renamed item starts over with the file's text, and the owner loses their edit. - **Types:** `text` (one line, up to 300 characters), `paragraph` (plain text, line breaks allowed, up to 2,000), `link` (https://…, /page, #section, mailto: or tel:), `phone`, `email`, `image` (a path under `public/` like `/images/hero.jpg`, or an https:// address), `seo_title`, `seo_description`. - **label:** what the owner sees, in plain words (“Home — main heading”, “Footer — opening hours”). - **page:** the page path where it shows, or `"*"` for the header, footer and contact details shown on every page. List every page in `"pages"`. - Values are plain text only: no HTML, no Markdown, no code. - Items in a list (services, team members, prices) each get their own keys: `services.1.title`, `services.1.text`, `services.2.title`… ## 3. Show content only through the One System helper The helper files are `onesystem/content.ts` and `onesystem/react.tsx` (from https://copilot.onesystemcrm.com/developers). ```tsx const title = useContent("home.hero.title"); return

{title}

; ``` - Always render values as text: `{value}`. Never use `dangerouslySetInnerHTML`, `innerHTML`, `eval`, or put a value into `style`, a class name, a script or a URL that isn't a `link`/`image` item. - Images the owner may swap live in `public/images/` (not imported from `src/`), referenced by the `image` item, with the `…_alt` item as the `alt` text. - `paragraph` values may contain line breaks: show them with CSS `white-space: pre-line`. ## 4. Setup (once per project) - **Site ID:** One System → Settings → Website → “Connect a site built with code” shows it (`os_` + 10 letters/digits). - Vite / React: `.env` → `VITE_ONESYSTEM_SITE=os_xxxxxxxxxx`, and in `index.html`: `` - Next.js: `.env` → `NEXT_PUBLIC_ONESYSTEM_SITE=os_xxxxxxxxxx`, and in `app/layout.tsx` metadata: `other: { "onesystem-site": process.env.NEXT_PUBLIC_ONESYSTEM_SITE ?? "" }` - **Vite / React** `src/main.tsx`: ```tsx import contentFile from "../public/onesystem-content.json"; import { OneSystemContent } from "../onesystem/react"; createRoot(document.getElementById("root")!).render( ); ``` On each page component call `useSeo("/about")` so the Google title and description come from the file. - **Next.js (App Router)** `app/layout.tsx`: ```tsx import contentFile from "@/public/onesystem-content.json"; import { loadContent, type ContentFile } from "@/onesystem/content"; import { OneSystemContent } from "@/onesystem/react"; const file = contentFile as ContentFile; export const revalidate = 30; export default async function RootLayout({ children }: { children: React.ReactNode }) { const initial = await loadContent({ site: process.env.NEXT_PUBLIC_ONESYSTEM_SITE, file, init: { next: { revalidate: 30 } } }); return {children}; } ``` Server components can call `loadContent(...)` the same way; Google titles go in `generateMetadata` from `seo..title` / `seo..description`. - Deploy as usual. Then in One System click **Check connection**. ## 5. When you change the site later Add new items to `public/onesystem-content.json` and deploy. One System adds them; the owner's earlier edits stay. Remove an item only when its element is removed from the site.