Getting Started
Quick Start
Build your first feature with @wayvo-ai/core in minutes. This guide walks you through creating a simple list page with CRUD operations.
Step 1: Define a DataSource
Create a DataSource definition for your entity:
// src/lib/server/ds/defs/EntityDS.ts
import type { DataSource } from '@wayvo-ai/core/common';
import { DefaultAttribute, DefaultDataSource, DefaultFullAccess } from '@wayvo-ai/core/ds';
export interface Entity {
id: string;
name: string;
description?: string;
createdAt: string;
}
export const EntityDataSource: DataSource<Entity> = {
...DefaultDataSource,
id: 'Entity',
tableName: 'entities',
attributes: [
{
...DefaultAttribute,
code: 'id',
name: 'ID',
type: 'Text',
column: 'id',
maxLength: 40,
primary: true,
defaultValue: 'ULID',
},
{
...DefaultAttribute,
code: 'name',
name: 'Name',
type: 'Text',
column: 'name',
maxLength: 120,
optional: false,
},
{
...DefaultAttribute,
code: 'description',
name: 'Description',
type: 'Text',
column: 'description',
maxLength: 500,
},
{
...DefaultAttribute,
code: 'createdAt',
name: 'Created At',
type: 'Date',
column: 'created_at',
optional: false,
},
],
access: [
{
...DefaultFullAccess,
roleCode: 'all_users',
},
],
};
Step 2: Create TypeScript Type
// src/lib/common/ds/types/Entity.ts
export interface Entity {
id: string;
name: string;
description?: string;
createdAt: string;
}
Step 3: Create Store Hook
// src/app/(secure)/entities/hooks/use-store.ts
import { useStore } from '@wayvo-ai/core/client';
import type { Entity } from '@/lib/common/ds/types/Entity';
export function useEntityStore() {
return useStore<Entity>({
datasourceId: 'Entity',
page: 'entity-page',
alias: 'entity-list',
limit: 20,
includeCount: true,
autoQuery: true,
sort: { createdAt: -1 },
});
}
Step 4: Create Table Columns
// src/app/(secure)/entities/hooks/use-table-columns.tsx
import type { Store } from '@wayvo-ai/core/common';
import type { Entity } from '@/lib/common/ds/types/Entity';
import type { AccessorKeyColumnDef } from '@tanstack/react-table';
import { HeaderCell, TableCell } from '@wayvo-ai/core/ui';
import { useMemo } from 'react';
export default function useTableColumns(store: Store<Entity>): AccessorKeyColumnDef<Entity>[] {
return useMemo(
() => [
{
accessorKey: 'name',
header: (props) => <HeaderCell {...props} type="Text" store={store} accessorKey="name" title="Name" />,
cell: (props) => <TableCell type="Text" attributeCode="name" {...props} />,
},
{
accessorKey: 'description',
header: (props) => (
<HeaderCell {...props} type="Text" store={store} accessorKey="description" title="Description" />
),
cell: (props) => <TableCell type="Text" attributeCode="description" {...props} />,
},
],
[store],
);
}
Step 5: Create Smart Search Columns
// src/app/(secure)/entities/hooks/smart-search-columns.ts
import type { Column } from '@wayvo-ai/core/ui/smart-search';
import type { Entity } from '@/lib/common/ds/types/Entity';
export default function useSmartSearchColumns(): Column<Entity>[] {
return [
{
key: 'name',
label: 'Name',
type: 'Text',
defaultOperator: 'is',
},
{
key: 'description',
label: 'Description',
type: 'Text',
defaultOperator: 'contains',
},
];
}
Step 6: Create Edit Form
// src/app/(secure)/entities/components/edit-form.tsx
'use client';
import { TextInput } from '@wayvo-ai/core/ui';
import { useCurrentRowSync, useIsStoreDirty, useIsStorePosting } from '@wayvo-ai/core/ui';
import type { Store } from '@wayvo-ai/core/common';
import type { Entity } from '@/lib/common/ds/types/Entity';
export function EditForm({ store }: { store: Store<Entity> }) {
const row = useCurrentRowSync(store);
const isDirty = useIsStoreDirty(store);
const isPosting = useIsStorePosting(store);
if (!row) return null;
return (
<div className="grid gap-4 p-4">
<TextInput
label="Name"
value={row.name || ''}
onChange={(value) => store.setValue('name', value)}
required
/>
<TextInput
label="Description"
value={row.description || ''}
onChange={(value) => store.setValue('description', value)}
/>
</div>
);
}
Step 7: Create Page Content
// src/app/(secure)/entities/page-content.tsx
'use client';
import { PageLayoutTemplate } from '@wayvo-ai/core/ui';
import { FileText } from 'lucide-react';
import { useEntityStore } from './hooks/use-store';
import useTableColumns from './hooks/use-table-columns';
import useSmartSearchColumns from './hooks/smart-search-columns';
import { EditForm } from './components/edit-form';
export default function EntityPageContent() {
const store = useEntityStore();
const tableColumns = useTableColumns(store);
const smartSearchColumns = useSmartSearchColumns();
return (
<PageLayoutTemplate
title="Entities"
subTitle="Manage your entities"
icon={<FileText className="h-12 w-12 text-muted-foreground" />}
store={store}
smartSearchColumns={smartSearchColumns}
tableColumns={tableColumns}
pageId="entity-page"
itemId="entity"
editForm={<EditForm store={store} />}
getDefaultRow={() => ({})}
addNewButtonText="Add Entity"
/>
);
}
Step 8: Create Page Entry Point
// src/app/(secure)/entities/page.tsx
'use client';
import { PageShell } from '@wayvo-ai/core/ui';
import dynamic from 'next/dynamic';
const PageContent = dynamic(() => import('./page-content'), { ssr: false });
export default function EntityPage() {
return (
<PageShell title="Entities" noPadding>
<PageContent />
</PageShell>
);
}
What You've Built
You now have a fully functional CRUD page with:
- ✅ List view with pagination
- ✅ Search and filtering
- ✅ Add/Edit dialog
- ✅ Automatic data synchronization
- ✅ Type-safe throughout
Next Steps
- DataSources - Learn more about data definitions
- Stores - Understand state management
- Components - Explore available components