Fragno is the framework-agnostic toolkit for building full-stack TypeScript libraries that work seamlessly across different frameworks such as Next.js, Nuxt, and way more.
With Fragno:
- Library authors write API routes and client-side hooks as part of their library.
- Users integrate a library into their application in a couple lines of code. They can then immediately use library functionality from both the frontend and the backend.
A library built with Fragno is called a Fragment.
A Fragment is a full-stack library that:
- Contains both server-side API logic and client-side integration code
- Works across multiple frameworks (React, Vue, Next.js, Nuxt, etc.)
- Provides end-to-end functionality: type-safe & reactive
Start building a full-stack library:
npm install @fragno-dev/core
# or
pnpm add @fragno-dev/coreFull documentation is available at fragno.dev.
- If you're looking to build a Fragment, see the For Library Authors section.
- If you're looking to integrate a Fragment into your project, see the User Quick Start section.
- π End-to-end type safety: routes are typed using Standard Schema and client-side hooks are fully type-safe
- π Built-in state management: reactive stores with caching built in (TanStack Query-style), using Nano Stores
- π Middleware support: Users can use middleware for custom request processing such as authentication.
- π Streaming support: Real-time data with NDJSON (New-line Delimited JSON) streaming
- π¦ Automatic code splitting: code is split on the library level, no added build complexity for end-users.
As a user of a Fragment (library built w/ Fragno), you integrate with only a couple of lines of code.
// app/api/example-fragment/[...all]/route.ts
import { createExampleFragment } from "@fragno-dev/example-fragment";
const exampleFragment = createExampleFragment({});
export const { GET, POST, PUT, PATCH, DELETE } = exampleFragment.handlersFor("next-js");Similar patterns apply for other frameworks as well.
On the frontend, the user gets fully-typed TanStack Query-style hooks.
// src/app/my-component.tsx
import { createExampleFragmentClient } from "@fragno-dev/example-fragment/react";
const { useTodos, useAddTodo } = createExampleFragmentClient();
export default function MyComponent() {
const { data: todos, loading: todosLoading } = useTodos();
const { mutate: addTodo, loading: addTodoLoading } = useAddTodo();
return (
<div>
{todos?.map(todo => (
<div key={todo.id}>{todo.text}</div>
))}
<button onClick={() => addTodo({ body: { text: "New todo" } })} disabled={addTodoLoading}>
{addTodoLoading ? "Adding..." : "Add Todo"}
</button>
</div>
);
}A Fragment is a full-stack library that works across different frameworks.
# Create from template
npm create fragno@latest
# or
pnpm create fragno@latest
# Add to existing package
npm install @fragno-dev/core
# or
pnpm add @fragno-dev/core// my-fragment/index.ts
import { defineFragment, defineRoute, createFragment } from "@fragno-dev/core";
import { createClientBuilder } from "@fragno-dev/core/client";
import { z } from "zod";
import OpenAI from "openai";
export interface FragmentConfig {
// Any arguments the Fragment's user will have to pass. Could be callback methods, AI model, etc.
openaiApiKey: string;
onTodoCreated?: (todo: { id: string; text: string; done: boolean }) => void;
}
// Define your fragment
const todosDefinition = defineFragment<FragmentConfig>("example-fragment").withDependencies(
(config: FragmentConfig) => {
return {
openaiClient: new OpenAI({
apiKey: config.openaiApiKey,
}),
};
},
);
// Define routes
const getTodosRoute = defineRoute({
method: "GET",
path: "/todos",
// Accepts any StandardSchema object
outputSchema: z.array(
z.object({
id: z.string(),
text: z.string(),
done: z.boolean(),
}),
),
handler: async (_, { json }) => {
return json([]);
},
});
const addTodoRoute = defineRoute({
method: "POST",
path: "/todos",
inputSchema: z.object({ text: z.string() }),
outputSchema: z.object({
id: z.string(),
text: z.string(),
done: z.boolean(),
}),
handler: async ({ input }, { json }) => {
const { text } = await input.valid();
const todo = { id: crypto.randomUUID(), text, done: false };
return json(todo);
},
});
// Export server creator
export function createTodos(serverConfig: FragmentConfig = {}, fragnoConfig = {}) {
return createFragment(todosDefinition, serverConfig, [getTodosRoute, addTodoRoute], fragnoConfig);
}
// Export type-safe client creator
export function createTodosClient(fragnoConfig = {}) {
const builder = createClientBuilder(todosDefinition, fragnoConfig, [getTodosRoute, addTodoRoute]);
return {
useTodos: builder.createHook("/todos"),
useAddTodo: builder.createMutator("POST", "/todos"),
};
}| Client-side Frameworks | Support | β | Server-side Frameworks | Support |
|---|---|---|---|---|
| React | β | Node.js / Express | β | |
| Vue | β | React Router v7 / Remix | β | |
| Vanilla JavaScript | β | Astro | β | |
| Svelte | β | Next.js | β | |
| SolidJS | β | Nuxt | β | |
| SvelteKit | β | |||
| SolidStart | β | |||
| TanStack Start | β |
See the Framework Support page for the full list of supported frameworks.
The example-apps directory contains complete examples of how to use Fragno with different
frameworks (React Router, Next.js, Nuxt, Astro, etc.).
There are also two examples showing the implementation of a Fragment (Fragno Library):
example-fragments/example-fragment- Minimal fragment exampleexample-fragments/chatno- Example fragment with OpenAI integration. Shows usage of config, dependencies, streaming, and advanced client-side state management.
Fragno is an open source project. We welcome contributions! Fragno is licensed under the MIT License. See the LICENSE file for details.
See the CONTRIBUTING file for details.