Builder.io Agent Native Best Practices: A Complete Implementation Guide
The best way to use Builder.io Agent Native is to treat the action surface as your single source of truth, persist all state in SQL via Drizzle, route third-party calls through the provider-api-catalog, and ensure both UI and agent invoke identical typed actions defined with defineAction.
Builder.io Agent Native tightly couples AI agents with UIs by having both invoke the same typed action surface. Following the architectural conventions defined in AGENTS.md and implemented in the core source code ensures your applications remain secure, type-safe, and maintainable. This guide covers the essential patterns for defining actions, securing data, and implementing UI components according to the framework's four pillars: Actions, Data, UI, and Agent Skills.
Core Architectural Principles
Unify on the Action Surface
Define every mutating operation as a named action using defineAction from @agent-native/core/actions within the actions/ directory. This creates a single source of truth that both your frontend and AI agent consume. The UI accesses these through useActionQuery and useActionMutation hooks, while agents invoke the same functions directly, ensuring behavioral parity across interfaces.
According to the AGENTS.md Four-Area Checklist in the BuilderIO/agent-native repository, this unified surface prevents logic duplication and maintains type safety across the stack.
Persist State in SQL via Drizzle
Store all persistent application state in SQL using Drizzle ORM. Reserve file or blob storage exclusively for large assets like images or documents, persisting only the resulting URLs or IDs in the database. This approach enables reliable transactions, consistent backups, and proper row-level security enforcement.
As documented in .agents/skills/storing-data/SKILL.md, this pattern ensures data integrity and simplifies access control compared to distributed state management.
Route External APIs Through the Provider Catalog
Access all third-party services through the provider-api-catalog (@agent-native/core/provider-api) rather than implementing custom REST wrappers. The catalog centralizes authentication, rate limiting, and request formatting for external services. Only bypass this catalog if the action surface lacks a required endpoint.
This convention is enforced in packages/core/src/provider-api/ and detailed in .agents/skills/provider-api-catalog/SKILL.md.
Implement UI with Type-Safe Primitives
Build user interfaces using TypeScript, shadcn/ui primitives, and Tabler Icons. Implement optimistic updates for mutations to ensure responsive interfaces. Avoid imperative browser APIs such as alert, prompt, or raw fetch calls within UI code; all data fetching must route through the typed action hooks.
The frontend design standards in .agents/skills/frontend-design/SKILL.md mandate this declarative approach to maintain consistency with the agent's capabilities.
Enforce Security at Every Layer
Never embed secrets or credentials in source code. Store all sensitive configuration in .env files or your platform's secret store. Guard sensitive routes using guard-no-env-credentials.mjs and enforce access controls at the SQL level through ownableColumns() filters.
The security implementation in packages/core/src/extensions/routes.ts and documented in .agents/skills/security/SKILL.md provides the regex parsers and guards necessary to block credential leaks before they reach production.
Implementation Patterns
Defining Strongly-Typed Actions
Create actions in the actions/ directory using Zod schemas for input validation. The following example from the BuilderIO/agent-native source demonstrates the pattern:
// actions/toggleFeature.ts
import { defineAction } from '@agent-native/core/actions';
import { z } from 'zod';
export const toggleFeature = defineAction({
name: 'toggleFeature',
input: z.object({
featureId: z.string(),
enable: z.boolean(),
}),
async handler({ input, ctx }) {
// Secure SQL write respecting ownableColumns()
await ctx.db
.update(table.features)
.set({ enabled: input.enable })
.where(eq(table.features.id, input.featureId));
return { success: true, featureId: input.featureId };
},
});
Consuming Actions in React Components
Import actions directly into your components and invoke them through useActionMutation. This ensures the UI uses the exact same logic and authorization checks as the agent:
import { useActionMutation } from '@agent-native/core/hooks';
import { toggleFeature } from '@/actions/toggleFeature';
export function FeatureSwitch({ featureId }: { featureId: string }) {
const { mutate, isLoading } = useActionMutation(toggleFeature);
return (
<Switch
disabled={isLoading}
onCheckedChange={(checked) =>
mutate({ featureId, enable: checked })
}
/>
);
}
Invoking Actions from Agent Skills
Agent skills should call the identical action surface used by the UI. Reference actions in skill documentation located in .agents/skills/:
// .agents/skills/feature-toggle/SKILL.md (excerpt)
When a user wants to toggle a feature, use the `toggleFeature` action:
{
"featureId": "checkout-promo",
"enable": true
}
The agent will invoke toggleFeature exactly as the React component would, ensuring consistent business logic.
Accessing Third-Party Services
Use callProvider from the provider-api-catalog to interact with external APIs:
import { callProvider } from '@agent-native/core/provider-api';
const result = await callProvider('stripe', {
method: 'POST',
path: '/v1/customers',
body: { email: user.email },
});
Extension Development
Building Sandboxed SQL-Backed Extensions
Develop sandboxed mini-applications as SQL-backed extensions that interact with the main app via appAction, dbQuery, and extensionFetch. This architecture prevents logic duplication and maintains isolation between extensions.
As detailed in .agents/skills/extensions/SKILL.md, extensions should leverage the database for state management rather than maintaining independent storage systems.
Testing and Validation
Running QA Scripts and Guards
Execute the provided smoke test suite after every change to verify action integrity and security posture. The scripts/qa-*.ts files, including qa-public-share-smoke.ts, validate that guards still block disallowed patterns and that actions behave correctly under various permission contexts.
Add unit tests for new actions to ensure handler logic respects row-level security and input validation schemas.
Summary
- Treat the action surface as canonical: Define all operations in
actions/usingdefineActionand consume them viauseActionMutationor direct agent invocation. - Persist data in SQL: Use Drizzle ORM for state management; limit blob storage to asset URLs.
- Centralize external APIs: Route all third-party calls through
@agent-native/core/provider-api. - Security hardening: Keep credentials in
.env, guard routes withguard-no-env-credentials.mjs, and enforce SQL-level filters. - Agent prompt engineering: Follow the Claude/Anthropic best-practice guidelines from
CHANGELOG.mdversion 6.4.38 by keeping prompts concise and including verification steps.
Frequently Asked Questions
How do I share logic between the UI and agent in Agent Native?
Define the logic once using defineAction in the actions/ directory and import it into both your React components (via useActionMutation) and your agent skills. This ensures both interfaces execute identical code paths with the same validation and security checks.
Where should I store API secrets in a Builder.io Agent Native app?
Store all secrets in .env files or your hosting platform's secret management system. Never commit credentials to source control. Use guard-no-env-credentials.mjs to programmatically validate that sensitive data does not leak into client bundles or logs.
Can I use raw fetch calls for external APIs?
No. Instead of raw fetch, use callProvider from @agent-native/core/provider-api. The provider catalog handles authentication, base URLs, and error formatting consistently across the application, whereas raw fetch calls bypass these security and standardization layers.
What testing strategy does Agent Native recommend?
Run the provided QA scripts in scripts/qa-*.ts after each deployment to smoke-test actions and security guards. Additionally, write unit tests for individual action handlers to verify they respect database filters and Zod input schemas, particularly for mutations affecting sensitive data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →