# Introduction > BaseHub is a fast and collaborative Headless CMS. Welcome to BaseHub Docs. [Throughout this site](https://docs.basehub.com/nextjs-integration/start-here), you’ll find instructions on how to connect BaseHub to your website using our SDKs, and you’ll learn more about our platform and features. ## Popular Sections Jump straight into one of our most popular sections. [Next.js Integration(Get started with Next.js)](https://docs.basehub.com/documentation/nextjs-integration/start-here)[API Reference(Know how our APIs work)](https://docs.basehub.com/api-reference) ## Help Have a specific question or support request? These are our support channels: * X: [https://x.com/basehub\_ai](https://x.com/basehub_ai) * Discord: [https://discord.gg/6Gk4qfuqHK](https://discord.gg/6Gk4qfuqHK) * Help Center (chat): [https://basehub.com/help?chat=true](https://basehub.com/help?chat=true) # Platform Overview > Understand the basics of the BaseHub Platform. BaseHub has three main properties: 1. The Dashboard, `basehub.com`, where you create teams, repositories, collaborate on content, etc 2. The GraphQ: API, `api.basehub.com`, where you interact with your repository programmatically, either to query data in your repo, or to mutate data in your repo 3. The SDK, which you install and run within your website: `pnpm i basehub` As you use BaseHub, you—or your team as a whole—will interact with all of these parts, and that’s why having a good understanding of the whole is important. ![](https://assets.basehub.com/7b31fb4b/wj_i1gS0peoNRJ_wze9ig/cleanshot-2024-05-24-at-16.56.032x.png?width=3840&quality=90&format=auto) ## Creating a Block Every piece of content you create in BaseHub is a Block. Similar to Lego, Blocks can have different types and functions. You can nest Blocks, reference Blocks, and more. In the Editor, you’ll create Blocks by typing `/` and choosing one Block type from the Block selector. Read more about Blocks in our Blocks Reference: [Blocks Reference(Deep dive into all of the different Blocks that are available in BaseHub.)](https://docs.basehub.com/blocks-reference) ## Committing A Commit stores a snapshot of your Repo at that specific point in time. Inspired by Git, each commit is immutable, and it’s a core of how version control works in BaseHub. Once you’re happy with your changes, you can create a Commit. The API will now use the latest commit (the Head Commit) to resolve your queries. ## Exploring the GraphQL API A great way to explore the GraphQL API is to use the Explorer. You can find it in the Developers Tab: ![](https://assets.basehub.com/7b31fb4b/5abd4deb5171f8df9b4bb848dcc4c20b/cleanshot-2025-01-20-at-19.23.462x.png?width=3840&quality=90&format=auto) # Start Here > Learn how to integrate your Next.js App with BaseHub in a couple of steps. ## Set Up `basehub` Our official JavaScript/TypeScript library exposes a CLI generator that, when run, will generate a type-safe GraphQL client. Check out [our API Reference](https://docs.basehub.com/api-reference/javascript-sdk) for more information. ### Install Install with your preferred package manager. npm ``` npm i basehub ``` ### Add the `BASEHUB_TOKEN` Environment Variable Get it from your BaseHub Repo’s “Connect to Your App” tab. .env.local ``` BASEHUB_TOKEN="" # Remember to also add this ^ env var in your deployment platform ``` ### Configure Node Scripts In order to generate the BaseHub SDK, we recommend running `basehub dev` in parallel to running the development server, and `basehub` right before building the app. package.json ``` "scripts": { "dev": "basehub dev & next dev", "build": "basehub && next build", "start": "next start", "lint": "next lint" }, ``` info: Using Windows? You might need to use something like `concurrently` instead of using the `&` to run a parallel node process. So: `concurrently \”basehub dev\” \”next dev\”` ### Start the Dev Server Give it a go to make sure the set up went correctly. npm ``` npm run dev ``` Now, let’s go ahead and query some content! ## Your First Query The recommended way to query content from BaseHub is with ``, a React Server Component that enables a Fast Refresh-like experience. app/page.tsx ``` import { Pump } from "basehub/react-pump" const Page = () => { return ( {async ([data]) => { "use server" return (
            {JSON.stringify(data, null, 2)}
          
) }}
) } export default Page ``` Notice we’re using Next.js’ `draftMode` and passing it down to Pump. You’ll learn more in the next section, but put briefly: when `draft === true`, Pump will subscribe to changes in real time from your Repo, and so keep your UI up-to-date. This is ideal for previewing content before pushing it to production. When `draft === false`, Pump will hit the Query API directly. # Querying Basics > Learn how to build GraphQL queries with the generated client. When you run `basehub`, you’ll be generating a GraphQL Client. What’s unique about this GraphQL client is that you’ll be defining the queries within your `.{js,ts}` files, instead of within `.graphql` ones. Most importantly, the output of your queries will be fully type safe. Getting runtime type safety is a huge DX boost. info: Under the hood, we use [https://genql.dev/](https://genql.dev/), so make sure you check out that project out. If you want to see how a GraphQL query converts to a GenQL query, you can [check out their converter tool](https://genql.dev/converter). ## `basehub()` This function let’s you fire off a single, direct query. Because of this, it’s perfect for **querying content that you don’t need to render**, like when defining `generateStaticParams` within a [dynamic Next.js page](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes). ``` import { basehub } from "basehub" export const generateStaticParams = async () => { const { posts } = await basehub().query({ posts: { items: { _slug: true } }, }) return posts.items.map((p) => { return { slug: p._slug } }) } ``` ## `` Pump can subscribe to realtime changes from your dashboard, and re-compute the JSX so that you can have a Fast Refresh-like experience. Because of this, Pump is ideal for querying content that you’ll want to render—for example, titles, images, rich texts, etc. ``` import { Pump } from "basehub/react-pump" import { draftMode } from "next/headers" const Page = async () => { return ( {async ([data]) => { "use server" return (
            {JSON.stringify(data, null, 2)}
          
) }}
) } export default Page ``` note: Under the hood, Pump uses basehub() as its client. You can think of Pump is an abstraction over the more primitive basehub(), that helps you get that realtime editing experience. With these two ways of querying in mind, let’s explore how to build our queries. ## Anatomy of a Query Queries are JavaScript objects, where each key represents a key in the GraphQL schema, and the value is a boolean which decides weather you want to retrieve that key or not. Let’s take this query as an example: ``` import { basehub } from "basehub" basehub().query({ _sys: { id: true, }, homepage: { title: true, }, posts: { items: { _id: true, _slug: true, _title: true, publishedAt: true, }, }, }) ``` This query above will return the following result: GraphQL Output ``` query { _sys { id } homepage { title } posts { items { _id _slug _title publishedAt } } } ``` As you can see, GraphQL and TypeScript are not _that different_, and this is what our client is taking advantage of. ### Passing arguments You can pass down arguments with `__args`: ``` import { basehub } from "basehub" basehub().query({ posts: { __args: { filter: { _sys_slug: { eq: "my-post-slug" }, }, }, items: { _id: true, _slug: true, _title: true, publishedAt: true, }, }, }) ``` ### Fragmenting Fragments are very useful to define data dependencies inside your application. To define a fragment with our SDK, you’ll use `fragmentOn`: ``` import { basehub, fragmentOn } from "basehub" export const PostFragment = fragmentOn("PostItem", { _id: true, _slug: true, _title: true, publishedAt: true, }) // you can use it as a type as well export type PostFragment = fragmentOn.infer basehub().query({ posts: { __args: { filter: { _sys_slug: { eq: "my-post-slug" }, }, }, items: { _id: true, _slug: true, _title: true, publishedAt: true, ...PostFragment }, }, }) ``` #### Co-Locating Components with Their Data Dependency A common pattern we enjoy using revolves around components defining thier own data dependencies. This works great with fragments, as we can easily define a fragment alongside a component and have it all be type safe. ``` // Let's imagine a Callout component: import { fragmentOn } from "basehub" import { RichText } from "basehub/react-rich-text" export const CalloutFragment = fragmentOn("CalloutComponent", { _id: true, emoji: true, body: { json: { content: true } }, }) export const Callout = ({ data, }: { data: fragmentOn.infer }) => { return (
{data.emoji} {data.body.json.content}
) } ``` Then you could use this `CalloutFragment` paired with `` , all type safe and with the data dependency co-located. If you update your Callout component and require more data from BaseHub, you can update the fragment and you’ll instantly get the data coming via props. ### Not Supported: Aliases Aliases are a very useful GraphQL feature, which unfortunately is not currently supported. If you need this feature, contact us to help us prioritize. # Rendering Rich Text > Fragments let you construct sets of fields, and then include them in queries where you need to. The GraphQL API can return your Rich Text Blocks’ data in multiple formats: 1. **Plain Text**, will ignore all formatting, media, and custom components, easy to render. 2. **HTML**, will ignore custom components, easy to render. 3. **Markdown**, will ignore custom components, needs a markdown to HTML parser to render. 4. **JSON**, comes with everything, but needs something that understand and processes it. In the case of the JSON format, the response will be an AST based on the [TipTap editor spec](https://tiptap.dev/docs/editor/guide/output#option-1-json). Because of the complexities associated with processing this JSON format, we’ve built a React Component called `` that will help us render our Rich Text content. This is how it works: ``` import { Pump } from "basehub/react-pump" import { RichText } from "basehub/react-rich-text" const Page = async () => { return ( {async ([{ homepage }]) => { "use server" return {homepage.subtitle.json.content} }} ) } export default Page ``` ## Component Overrides When using the `` component, you can simply pass the JSON content into it via `children`, and it’ll get rendered. If you want to use a custom handler for a certain HTML node (imagine using Next.js’ `` to render images), you’d use the `components` prop. ``` import { Pump } from "basehub/react-pump" import { RichText } from "basehub/react-rich-text" import Image from "next/image" const Page = async () => { return ( {async ([{ homepage }]) => { "use server" return ( , }} > {homepage.subtitle.json.content} ) }} ) } export default Page ``` note: `` will return the HTML for each node of content, without any `
` wrapping everything nor any styles. We recommend using something like [Tailwind Typography](https://tailwindcss.com/docs/typography-plugin#installation) for quick prose styling, or of course, writing your own CSS. ## Custom Components If you are using [Custom Blocks in your Rich Text](https://basehub.com/changelog/instantiate-components-inside-rich-text-blocks), you’ll need to add them to your query, and pass them via the `blocks` prop. Then, you’ll be able to set up the custom renderers for them (in a type-safe manner, by the way): ``` import { Pump } from "basehub/react-pump" import { RichText } from "basehub/react-rich-text" import Image from "next/image" import { Callout, CodeSnippet } from './path-to/components' const Page = async () => { return ( {async ([{ homepage }]) => { "use server" return ( , CalloutComponent: (props) => , CodeSnippetComponent: (props) => , }} > {homepage.subtitle.json.content} ) }} ) } export default Page ``` We hope this removes a bit of friction from the sill tough task of rendering Rich Text data. ## Internal Links Similar to external hyperlinks, internal links in your rich text let you reference other blocks in an easy and type-safe manner. ``` import { Pump } from "basehub/react-pump" import { RichText } from "basehub/react-rich-text" import Image from "next/image" const Page = async () => { return ( {async ([{ homepage }]) => { "use server" return ( , a: ({ internal, ...props }) => { if (internal) { switch (internal.__typename) { case "PostComponent": return ( ) default: return null } } else return }, }} > {homepage.subtitle.json.content} ) }} ) } export default Page ``` # Environments & Caching > Understand the different environments and caching strategies you can leverage to improve your content editing experience. ## Environments Setting up your application so that it handles all of the environments in a seamless fashion is a very important part of integrating with BaseHub. ### Local Environment When developing in localhost, you’ll be writing new code and iterating over your content. This means adding new blocks, changing those blocks’ constraints, writing content and more—all at the same time. In order to _not break your flow_, you’ll want two things: 1. For the schema in BaseHub to be in sync with your IDE, and 2. For the content to update as you write, without needing to commit it yet. We bundled these two needs into one command: ``` basehub dev ``` This command generates the type-safe SDK and keeps it in sync with changes you make in basehub.com (this is called `--watch` mode); and also sets up the SDK so that it queries Draft content from your repository. This is why we recommend you run it _in parallel_ to `next dev`. ``` "scripts": { "dev": "basehub dev & next dev", "build": "basehub && next build", "start": "next start", "lint": "next lint" }, ``` _Notice the single_ `&`_._ ### Preview Environment Setting up an easy way for editors to preview content before committing it into production is essential. We’ve designed our preview workflow with these three pillars in mind: 1. Content should render in real time, as you write. 2. Preview should be easy for developers to integrate. 3. The integration should never degrade production performance _in any way_. We achieve this is by using a couple of BaseHub components, `` and ``, in combination to Next.js’ `draftMode`. Additionally, to bridge the gap between basehub.com (the dashboard) and your website, you’ll need to set up the “Preview” Button. \[data-radix-scroll-area-viewport\]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}\[data-radix-scroll-area-viewport\]::-webkit-scrollbar{display:none} Name Description Author `` Queries the API. Receives a `draft` prop that controls weather it’ll hit draft content and subscribe to real time changes, or just hit production. BaseHub `draftMode` Allows you to detect Draft Mode inside a Server Component. Next.js `` Helper to turn on/off Draft Mode within your site, with zero-config. BaseHub Preview Button Links from a BaseHub block into where that block is being rendered in your website. BaseHub This is how a simple code example can look like: app/layout.tsx ``` import { Toolbar } from 'basehub/next-toolbar' export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( {/* Layout UI */}
{children}
) } ``` Finally, we need to set up our Preview Buttons. See how to set up Preview Buttons, and the whole preview environment really. ### Production Environment Once we’ve set up Local and Preview environments, most of the hard work is done. The only thing you need to make sure when going to production is for the SDK to be generated before the build step of your application. ``` "scripts": { "dev": "basehub dev & next dev", "build": "basehub && next build", "start": "next start", "lint": "next lint" }, ``` That should be it. You’re ready to deploy your website. warning: Using [Turborepo](https://turbo.build/repo) or a similar monorepo manager? You need to add `.basehub` to `turbo.json`'s build outputs, so it can keep track of schema changes. ``` "tasks": { "build": { "outputs": [".basehub/**"] },   } ``` ## Caching By default, [Next.js will try to cache all of our requests made with](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#opting-out-of-data-caching) `fetch`—and that includes BaseHub. While this makes subsequent requests to BaseHub much faster, it’ll essentially make your website’s content fully static, instead of reflecting the latest content changes from your BaseHub Repo. This introduces a new task for the developer, which is to purge that cache when content from BaseHub changes. These are some of the options you have: ### On-Demand Revalidation (Recommended) The absolute best method of revalidation is “on-demand”. As its name implies, it consists of purging the cached data at the exact moment a change occurs. This provides the best experience for editors, as they won’t need to refresh the website for several seconds to see their content live; and also keeps server costs down, as the server itself won’t need to constantly check with our API to see if something has changed. BaseHub provides automatic on-demand revalidation in a fine-grained manner. * Automatic: without the need of constant developer setup. * Fine-grained: with every query being revalidated individually—in contrast to an “all or nothing” approach. This is how: ### Mount the in `layout.tsx` This will add a Server Action to revalidate the specific tags `basehub()` will set to each query. ``` // app/layout.tsx import { Toolbar } from "basehub/next-toolbar" export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( {/* Layout UI */}
{children}
) } ``` ### Fill in the Website URL input in BaseHub This will help our servers know where to go to to revalidate the queries. ![](https://assets.basehub.com/7b31fb4b/4dc74fb9bbf1a5d52c36b5649b537665/cleanshot-2024-10-12-at-11.04.212x.png?width=3840&quality=90&format=auto) In the Readme, top right ### Just use it That should be all! Make sure you don’t pass other cache-related props (such as `revalidate` or `cache`), as that will opt the query out of automatic on-demand revalidation. As you may notice, we’re also not passing `draftMode().isEnabled` via props, as this is no longer required as of `basehub@7.5.10`—we now automatically infer draft mode for you. ``` import { Pump } from "basehub/react-pump" import { basehub } from "basehub" const Page = async () => { // works with basehub and with Pump const data = await basehub().query({ __typename: true }) return ( {async ([data]) => { "use server" return
{JSON.stringify(data, null, 2)}
}}
) } export default Page ``` info: Wondering **how does this all work?** When `basehub().query` is ran, we hash the query being sent and use it as a cache tag. We send this cache tag to our servers (alongside the query itself). The server now runs the query and computes the response. It will then hash the response, and store the cache tag, the original query, and the response hash in our database. On commit, we’ll get all of the queries we’ve been collecting and run them again against the newly committed tree of blocks. Now, one by one, we run them, compute the response hash, and compare it against the one we previously returned to the user. If response hashes don’t match, we need to revalidate the query. To revalidate the query, we spin up a headless browser that navigates to your Website URL and executes the Server Action our `` created. Read the [full writeup in our blog](https://basehub.com/blog/automatic-on-demand-revalidation-for-nextjs-how-it-works) to learn more. ### Time-Based Revalidation Another conventional way to revalidate content is to use Next.js’ [time-based](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#time-based-revalidation) `revalidate` [caching option](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#time-based-revalidation). ``` import { Pump } from "basehub/react-pump" const Page = async () => { return ( {async ([data]) => { "use server" // `data` will be stale after 30 seconds return
{JSON.stringify(data, null, 2)}
}}
) } export default Page ``` While this is very easy to set up, automatic on-demand revalidation is always better, as editors won’t need to refresh the website for several seconds to see their content live; will keep server costs down, as the server itself won’t need to constantly check with our API to see if something has changed; and will simply remove one task from developers’ hands. # MCP > You can use your agent tools provided by our MCP server and connect it to your favorite apps, like Claude and Cursor. The MCP (Model Context Protocol) integration enables AI agents to interact directly with your BaseHub repository through a comprehensive set of tools. From creating and updating content blocks to managing assets and automating workflows, you can build functional websites, migrate hardcoded content, and set up forms—all through natural language prompts with your favorite AI tools. info: “MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.” [More on Anthropic →](https://modelcontextprotocol.io/introduction) ## How to integrate ### Prerequsities To use the BaseHub MCP, you should have [Cursor](https://cursor.com/) installed in your machine before integrating it. ### Go to “Connect” tab in your dashboard ![](https://assets.basehub.com/7b31fb4b/14d99cd919c63be1e7e3cb3adc872eb8/screenshot-2025-07-14-at-11.50.37-am.png?width=3840&quality=90&format=auto) Developers -> Connect to your app -> Add to Cursor ### Click on “Add to Cursor” The flow will ask you to open the “Cursor” app. After opening, it will show this config form. ![](https://assets.basehub.com/7b31fb4b/bb499c7d7575a766256cee3ab6386e6e/screenshot-2025-07-14-at-11.59.59-am.png?width=3840&quality=90&format=auto) The default name always includes your repository name. This enables having multiple MCP Servers targeting different repositories. ### Finish your set-up Rename the MCP server if you need and click “Install”. You’re ready to start building with Cursor + BaseHub. NOTE: Make sure to use Agent mode to let Cursor use BaseHub tools. note: In the background, this button adds the BaseHub MCP server URL to your Cursor config with a special BaseHub MCP Token that is linked to your user in BaseHub. This token gives read and write access to the LLM and saves the current ref where the LLM will work (`main` by default). To set it up in Claude desktop, you should copy and paste the BaseHub MCP token by yourself. ## Available tools \[data-radix-scroll-area-viewport\]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}\[data-radix-scroll-area-viewport\]::-webkit-scrollbar{display:none} Tool Name Description `query_content` Query the BaseHub repository content. Use this as you need to get content created by the user, or specific IDs for subsequent content changes. `create_blocks` Create one or more BaseHub blocks (with possible nested children). `update_blocks` Update existing blocks in BaseHub. `delete_blocks` Delete one or more BaseHub blocks in a single transaction. Only requires the block ID. `commit` Create a new commit in BaseHub, publishing all draft changes. `merge_branch` Merge a BaseHub branch into another branch. `create_branch` Create a new branch based on an existing branch in BaseHub. The new branch will be created from the specified base branch and optionally checked out. `checkout_branch` Checkout (switch to) a specific branch in BaseHub. NOTE: This changes the current working branch to the specified branch name for the Agent using the MCP, not for the user. `get_current_ref` Get the current BaseHub branch that the LLM is using. `list_branches` List all branches in the current BaseHub repository. `get_upload_url` Returns a signed URL where the LLM can upload assets to BaseHub. `get_content_structure` Retrieve the structure of the current BaseHub repository in XML format and possible block types. `get_example_content_structure` Get an XML representation of the structure of one of BaseHub example repos. This serves as inspiration on how to create structures in BaseHub. `get_query_guidelines` A helper tool that tells the LLM how to structure queries. `get_mutation_guidelines` A helper tool that tells the LLM how to structure mutations. `search_developer_docs` Search the BaseHub developer docs. `get_token` Get the repository read/write tokens. ## Use cases ### Start from scratch, create a fresh new website. You can have a functional website in minutes with a good prompt and the help of BaseHub tools. ### Un-hardcode an existing piece of content from your site. Incrementally implementing your content into the CMS has never been easier, push hardcoded content into BaseHub with just one prompt. ### Upload local images/videos/3D-models to your CMS. The MCP supports asset uploading and lets the LLM know which steps to take to easily upload local files to BaseHub CDN ### Set up a newsletter form (or any kind of form) without writing a single line of code. Simplify your integrations with LLMs work and BaseHub API transaction capabilities. # Agents > BaseHub Agents are AI assistants that help with content management, automated workflows, and team collaboration. ## START It comes preconfigured with: * An extensive system prompt optimized for BaseHub workflows and content operations * All available tools enabled, including content editing, users data, and web access * Deep integration with BaseHub's ecosystem, understanding your content structure and relationships ![](https://assets.basehub.com/7b31fb4b/59f1a39e9e9965cc156936cb77c6528f/image?width=3840&quality=90&format=auto) ## Comments You will see START in the users list when typing `@` in your comments. You can use `@START` to include the agent in the conversation. Agents in comments have contextual awareness, they will know on which block the comment is and the previous conversation in the thread, just like another user! ![](https://assets.basehub.com/7b31fb4b/193a43804b2cee819f8bb002a41756fd/image?width=3840&quality=90&format=auto) As you can see in the conversation, once mentioned, the agent knows it needs to give an answer and assumes the context based on where the comment is located ## Agents BaseHub Agents (currently in free beta) are AI companions that can be customized through your dashboard to help with content management, automated workflows, and team collaboration. Whether you need contextual assistance in comments, automated content updates, or external integrations with platforms like Slack and Claude, agents can be tailored to your specific needs and workflows. Step by step creating an Agent and customizing its properties ### External usage #### Embed chat You can embed your chat in any application with a simple snippet provided in the connect instructions above your agent tab. app/layout.tsx ``` import Script from 'next/script' import { basehub } from 'basehub' import '../basehub.config' export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { const data = await basehub().query({ _agents: { docsAi: { embedUrl: true, }, }, }) return (
{children}