Skip to main content

Basic Usage

After running bunx readonly sync, your files are available for import:
import { documents, config, redirects } from "@readonly/client";

console.log(documents.title);
console.log(config.apiUrl);
console.log(redirects["/old-path"]);
All imports are fully typed with IntelliSense support!

Type Safety

ReadOnly automatically generates TypeScript types by introspecting your JSON files.

Example

Given this JSON file named config:
{
	"apiUrl": "https://api.example.com",
	"timeout": 5000,
	"retries": 3,
	"features": {
		"darkMode": true,
		"analytics": false
	},
	"endpoints": ["/users", "/posts", "/comments"]
}
ReadOnly generates this type:
export declare const config: {
	apiUrl: string;
	timeout: number;
	retries: number;
	features: {
		darkMode: boolean;
		analytics: boolean;
	};
	endpoints: string[];
};

IntelliSense

Your editor will provide:
  • Auto-completion for all properties
  • Type checking
  • Inline documentation
  • Go-to-definition support
IntelliSense example

Import Patterns

Named Imports

Import specific files:
import { documents } from "@readonly/client";
import { config } from "@readonly/client";
import { redirects } from "@readonly/client";

Wildcard Import

Import all files at once:
import * as readonly from "@readonly/client";

console.log(readonly.documents);
console.log(readonly.config);

Re-exporting

Re-export for use throughout your app:
// lib/readonly.ts
export { documents, config, redirects } from "@readonly/client";

// elsewhere in your app
import { documents } from "@/lib/readonly";

Common Use Cases

1. Configuration

Store app configuration in ReadOnly:
import { config } from "@readonly/client";

const api = axios.create({
	baseURL: config.apiUrl,
	timeout: config.timeout,
});

2. Content Management

Manage blog posts or documentation:
import { posts } from "@readonly/client";

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

3. Redirects

Handle URL redirects:
import { redirects } from "@readonly/client";

export function middleware(request: Request) {
	const path = new URL(request.url).pathname;
	const redirect = redirects[path];

	if (redirect) {
		return Response.redirect(redirect, 301);
	}
}

4. Feature Flags

Manage feature flags:
import { features } from "@readonly/client";

export function App() {
  return (
    <div>
      {features.darkMode && <DarkModeToggle />}
      {features.analytics && <Analytics />}
    </div>
  );
}

5. Reference Data

Store reference data like countries, currencies, etc:
import { countries } from "@readonly/client";

export function CountrySelect() {
  return (
    <select>
      {countries.map(country => (
        <option key={country.code} value={country.code}>
          {country.name}
        </option>
      ))}
    </select>
  );
}

Framework Examples

Next.js

// app/page.tsx
import { documents } from "@readonly/client";

export default function Home() {
  return (
    <div>
      <h1>{documents.title}</h1>
      <p>{documents.description}</p>
    </div>
  );
}

Express

// server.ts
import express from "express";
import { config } from "@readonly/client";

const app = express();

app.listen(config.port, () => {
	console.log(`Server running on port ${config.port}`);
});

Remix

// app/routes/_index.tsx
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { documents } from "@readonly/client";

export const loader = () => {
  return json({ documents });
};

export default function Index() {
  const { documents } = useLoaderData<typeof loader>();
  return <h1>{documents.title}</h1>;
}

Astro

---
// src/pages/index.astro
import { documents } from "@readonly/client";
---

<html>
  <body>
    <h1>{documents.title}</h1>
    <p>{documents.description}</p>
  </body>
</html>

Runtime vs Build Time

Runtime Loading

Files are loaded at runtime from the cache:
// This loads from node_modules/.cache/readonly/documents.json
import { documents } from "@readonly/client";

Build Time Sync

Sync during build to include files in your bundle:
package.json
{
	"scripts": {
		"build": "readonly sync && next build"
	}
}

File Updates

Development

Use watch mode to automatically sync updates:
bunx readonly dev
When you update files in the dashboard, they’ll sync automatically.

Production

Run watch mode alongside your app:
package.json
{
	"scripts": {
		"start": "concurrently \"node server.js\" \"readonly dev\""
	}
}

Best Practices

Individual files are limited to 10MB. Keep files focused and avoid large datasets.
Name your files descriptively: config, features, redirects, not data1, file2.
Ensure your JSON is valid and follows a consistent structure. ReadOnly validates JSON objects only (no arrays or primitives at root).
Always run readonly sync before building for production to ensure files are up-to-date.
Run readonly dev alongside your dev server to automatically sync changes.

Next Steps