Skip to content

React Hooks

The @githosted/sdk/react entrypoint provides TanStack Query hooks for building React UIs on top of githosted repos.

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
GithostedProvider,
useFileTree,
useFile,
} from "@githosted/sdk/react";
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<GithostedProvider token="gw_xxx">
<FileBrowser />
</GithostedProvider>
</QueryClientProvider>
);
}
function FileBrowser() {
const { data: files } = useFileTree("my-project", "src/");
return (
<ul>
{files?.map((f) => (
<li key={f.name}>
{f.type === "directory" ? "📁" : "📄"} {f.name}
</li>
))}
</ul>
);
}
Terminal window
npm install @githosted/sdk @tanstack/react-query react

GithostedProvider requires an outer QueryClientProvider — it does not create its own QueryClient. This avoids fragmented caches and gives you full control over query defaults.

<QueryClientProvider client={queryClient}>
<GithostedProvider token="gw_xxx" baseUrl="https://api.githosted.dev">
{children}
</GithostedProvider>
</QueryClientProvider>

You can also pass a pre-built Client:

import { Client } from "@githosted/sdk";
const client = new Client({ token: "gw_xxx" });
<GithostedProvider client={client}>
{children}
</GithostedProvider>

List files at a path.

const { data, isLoading } = useFileTree(
{ id: "rp_xxx" }, // or "my-project"
"src/",
{ ref: "main" },
);

Read a file’s content and metadata. Returns { content, headSha, blobSha }.

const { data: file } = useFile({ id: "rp_xxx" }, "src/main.ts", {
ref: "main",
});
// file?.content, file?.headSha

Get the commit history.

const { data: commits } = useCommitLog({ id: "rp_xxx" }, { limit: 20 });

List branches.

const { data: branches } = useBranches({ id: "rp_xxx" });

Write a file. Automatically invalidates file tree, file content, commit log, and branch caches on success.

const { mutateAsync: writeFile } = useWriteFile({ id: "rp_xxx" });
const handleSave = async () => {
await writeFile({
path: "src/main.ts",
content: editedCode,
message: "Edit via browser",
expectedHead: file?.headSha,
});
};

Delete a file.

const { mutateAsync: deleteFile } = useDeleteFile({ id: "rp_xxx" });
await deleteFile({ path: "src/old.ts", message: "Remove old file" });

Atomic multi-file commit.

const { mutateAsync: commit } = useTransaction({ id: "rp_xxx" });
await commit({
message: "Refactor",
changes: [
{ action: "write", path: "src/new.ts", content: code },
{ action: "delete", path: "src/old.ts" },
],
});

All hooks accept a RepoRef — either a slug string or { id: "rp_xxx" }:

useFileTree("my-project", "src/"); // by slug
useFileTree({ id: "rp_8f3k2m1q9t6w4z7c5n2h" }, "src/"); // by stable ID

Use stable IDs for apps that persist repo references.

All query keys are prefixed with ["githosted", clientScope, repoRef, hookName, ...] where clientScope is derived from the client’s baseUrl + token. This prevents cache bleed between different providers sharing the same QueryClient.

When the token changes, the scope changes and all previous queries are invalidated.

Mutation hooks automatically invalidate affected queries:

After a write…Invalidated
File treeuseFileTree for parent directory
File contentuseFile for the exact path + ref
Commit loguseCommitLog for the repo
Branch stateuseBranches for the repo
import { ConnectError, Code } from "@connectrpc/connect";
import { RepoBusyError } from "@githosted/sdk/react";
<GithostedProvider
token={token}
onError={(error) => {
// onError receives a plain Error. Check for ConnectError to inspect codes.
if (error instanceof ConnectError && error.code === Code.Unauthenticated) {
router.navigate("/login");
}
}}
onRepoBusy={(error, attempt) => {
// Called on each auto-retry attempt for repo_busy errors.
toast(`Repo busy (${error.operation}), retrying... (attempt ${attempt})`);
}}
>