Blob storage provides zero-configuration persistent storage for your GenSX applications. It enables you to store JSON, text, or binary data for your agents and workflows without worrying about managing infrastructure.
To use blob storage in your GenSX application:
Install the storage package:
npm install @gensx/storage
Next.js Configuration (if using Next.js): Add the following webpack configuration to your next.config.ts
or next.config.js
file:
/** @type {import('next').NextConfig} */
const nextConfig = {
// ... other config options
webpack: (config: any) => {
// Ignore @libsql/client package for client-side builds
config.resolve.alias = {
...config.resolve.alias,
"@libsql/client": false,
};
return config;
},
// ... other config options
};
module.exports = nextConfig;
This configuration prevents bundling issues while allowing the storage hooks to work properly in server components and API routes. See the client-side-tools example for a complete implementation.
Access blobs within your components using the useBlob
hook:
import { useBlob } from "@gensx/storage";
const blob = useBlob("your-key.json");
The useBlob
hook provides simple methods to read different types of data:
import { useBlob } from "@gensx/storage";
// Read JSON data
const profileBlob = useBlob("users/profile.json");
const profile = await profileBlob.getJSON();
console.log(profile?.name);
// Read text data
const notesBlob = useBlob("notes/meeting.txt");
const notes = await notesBlob.getString();
// Read binary data
const imageBlob = useBlob("images/photo.jpg");
const image = await imageBlob.getRaw();
console.log(image?.contentType); // "image/jpeg"
You can write data in various formats:
import { useBlob } from "@gensx/storage";
// Write JSON data
const profileBlob = useBlob("users/profile.json");
await profileBlob.putJSON({ name: "Alice", preferences: { theme: "dark" } });
// Write text data
const notesBlob = useBlob("notes/meeting.txt");
await notesBlob.putString(
"Meeting agenda:\n1. Project updates\n2. Action items",
);
// Write binary data
const imageBlob = useBlob("images/photo.jpg");
await imageBlob.putRaw(imageBuffer, {
contentType: "image/jpeg",
metadata: { originalName: "vacation.jpg" },
});
One of the most common use cases for blob storage is maintaining conversation history across multiple interactions:
import * as gensx from "@gensx/core";
import { openai } from "@ai-sdk/openai";
import { useBlob } from "@gensx/storage";
import { generateText } from "@gensx/vercel-ai";
interface ChatMessage {
role: "system" | "user" | "assistant";
content: string;
}
const ChatWithMemory = gensx.Component(
"ChatWithMemory",
async ({ userInput, threadId }: ChatInput) => {
// Get a reference to the thread's storage
const blob = useBlob<ChatMessage[]>(`chats/${threadId}.json`);
// Load existing messages or start with a system prompt
const messages = (await blob.getJSON()) ?? [
{
role: "system",
content: "You are a helpful assistant.",
},
];
// Add the new user message
messages.push({ role: "user", content: userInput });
// Generate a response using the full conversation history
const result = await generateText({
messages,
model: openai("gpt-4.1-mini"),
});
// Save the assistant's response to the history
messages.push({ role: "assistant", content: result.text });
await blob.putJSON(messages);
return result.text;
},
);
For more complex agents, you can store structured memory:
interface AgentMemory {
facts: string[];
tasks: { description: string; completed: boolean }[];
lastUpdated: string;
}
const AgentWithMemory = gensx.Component(
"AgentWithMemory",
async ({ input, agentId }: AgentInput) => {
// Load agent memory
const memoryBlob = useBlob<AgentMemory>(`agents/${agentId}/memory.json`);
const memory = (await memoryBlob.getJSON()) ?? {
facts: [],
tasks: [],
lastUpdated: new Date().toISOString(),
};
// Process input using memory
// ...
// Update and save memory
memory.facts.push("New fact learned from input");
memory.tasks.push({ description: "Follow up on X", completed: false });
memory.lastUpdated = new Date().toISOString();
await memoryBlob.putJSON(memory);
return "Response that uses memory context";
},
);
You can use blob storage to save and retrieve binary files like images:
const StoreImage = gensx.Component(
"StoreImage",
async ({ imageBuffer, filename }: StoreImageInput) => {
const imageBlob = useBlob(`images/${filename}`);
// Save image with metadata
await imageBlob.putRaw(imageBuffer, {
contentType: "image/png",
metadata: {
uploadedAt: new Date().toISOString(),
pixelSize: "800x600",
},
});
return { success: true, path: `images/${filename}` };
},
);
const GetImage = gensx.Component(
"GetImage",
async ({ filename }: GetImageInput) => {
const imageBlob = useBlob(`images/${filename}`);
// Check if image exists
const exists = await imageBlob.exists();
if (!exists) {
return { found: false };
}
// Get the image with metadata
const image = await imageBlob.getRaw();
return {
found: true,
data: image?.content,
contentType: image?.contentType,
metadata: image?.metadata,
};
},
);
For scenarios where multiple processes might update the same data, you can use ETags to prevent conflicts:
const UpdateCounter = gensx.Component(
"UpdateCounter",
async ({ counterName }: UpdateCounterInput) => {
const blob = useBlob(`counters/${counterName}.json`);
// Get current value and metadata
const counter = (await blob.getJSON<{ value: number }>()) ?? { value: 0 };
const metadata = await blob.getMetadata();
// Update counter
counter.value += 1;
try {
// Save with ETag to prevent conflicts
await blob.putJSON(counter, {
etag: metadata?.etag,
});
return { success: true, value: counter.value };
} catch (error) {
if (error.name === "BlobConflictError") {
return {
success: false,
message: "Counter was updated by another process",
};
}
throw error;
}
},
);
GenSX blob storage works identically in both local development and cloud environments:
.gensx/blobs
directory by defaultIf you don’t specify a “kind” that the framework auto-infers this value for you based on the runtime environment.
No code changes are needed when moving from development to production.
See the blob storage component reference for full details.