@ragrabbit/mcp
by madarco
Verified
Code Style and Structure:
- Write concise, technical TypeScript code with accurate examples
- Prefer iteration and modularization over code duplication
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Fullstack development:
- The main app is saas/app
- Use the packages/auth package for authentication, which exposes an auth function, eg: const session = await authOrLogin(); const email = session.user.email
- Put the components for the UI in the packages/design package: put them in packages/design/components/_section name_/
- Put the data schema in the packages/db package using DrizzleORM and PostgreSQL. The db can be acquired with `import db from "@repo/db"`, the schema with `import { usersTable } from "@repo/db/schema"`, the DrizzleOrm utilities with `import { eq } from "@repo/db/drizzle"`
- When updating the db schema, run `cd apps/saas && pnpm drizzle-kit generate` to generate the new migrations while `pnpm drizzle-kit migrate` to apply them to the db.
- For API routes prefer using Server Actions in the apps/saas app directly, inside the same folder as the page and route
- For Server Actions, uses the this uses next-safe-action package and prefer using the authActionClient from the packages/actions package, eg: const result = await authActionClient.schema(mySchema).metadata({ name: "myAction" }).action(async ({ parsedInput, ctx }) => { ... });
- Calling a Server Action from a page can be done directly eg: const { data: result } = await myAction({}); or const { executeAsync} = useAction(myAction); const { data } = await executeAsync();
Naming Conventions:
- Use lowercase with dashes for directories (e.g., components/auth-wizard)
- Favor named exports for components
TypeScript Usage:
- Use TypeScript for all code; prefer interfaces over types
- Avoid enums; use maps instead
- Use functional components with TypeScript interfaces, eg `export default function MyComponent({ myProp }: { myProp: string }) { ... }`
Syntax and Formatting:
- Use the "function" keyword for pure functions
- Use declarative JSX
Error Handling and Validation:
- Prioritize error handling: handle errors and edge cases early
- Use early returns and guard clauses
- Implement proper error logging and user-friendly messages
- Use Zod for form validation
- Model expected errors as return values in Server Actions
- Use error boundaries for unexpected errors
UI and Styling:
- Use Shadcn UI, Radix, and Tailwind Aria for components and styling
- Implement responsive design with Tailwind CSS; use a mobile-first approach
- Put the components for the UI in the @repo/design/ package
- Use the cn function for tailwind classes from @repo/design/lib/utils
- Use the import "@repo/design/..." always instead of @/, eg import { Button } from "@repo/design/shadcn/button"
- To import design components, use the path: "@repo/design/components/...". For Shadcn components, use the path: "@repo/design/shadcn/..."
- To import icons, use the path: "@repo/design/base/icons" this exposes lucide-react icons.
- Always generate also a Storybook story for each component, put it next to the component file.
- For Forms, use the EasyForm component from the @repo/design/components/form package.
An Example Storybook story:
```
import { Meta, StoryObj } from "@storybook/react";
import { Badge } from "../badge";
const meta: Meta<typeof Badge> = {
title: "UI/Custom/Badge",
component: Badge,
argTypes: {
variant: {
control: "select",
options: ["default", "primary", "secondary", "success", "warning", "danger"],
},
},
};
export default meta;
type Story = StoryObj<typeof Badge>;
export const Default: Story = {
args: {
children: "Default Badge",
},
};
```
And example of an EasyForm:
```
const form = useForm<IndexFormValues>({
resolver: zodResolver(addIndexSchema),
defaultValues: { urls: [{ value: "" }] },
mode: "onChange",
});
// Multi field array:
const urlsFieldArray = useFieldArray({
name: "urls",
control: form.control,
});
<EasyForm form={form} onSubmit={onSubmit} message="Form submitted successfully">
<EasyFormFieldText
form={form}
name="name"
title="Name"
description="Please enter your full name"
placeholder="John Doe"
/>
<EasyFormFieldText
form={form}
name="email"
title="Email"
description="Enter your email address"
placeholder="john@example.com"
/>
<EasyFormMultiTextField
form={form}
field={field}
name="multiField"
title="Multi Field"
description="This is a multi EasyFormFieldText component"
placeholder="Enter some text"
/>
<EasyFormFieldSwitch form={form} name="switch" title="Switch" label="Switch Label" />
<EasyFormSubmit form={form} isExecuting={false} />
</EasyForm>
```
An example of a Server Action actions.ts file:
```
import { addIndexSchema } from "./schema.actions";
export const addIndex = authActionClient.schema(addIndexSchema).metadata({ name: "addIndex" }).action(async ({ parsedInput, ctx }) => {
// ...
});
```
and add a schema file for the actions: schema.actions.ts:
```
export const addIndexSchema = z.object({
urls: z.array(z.string()).min(1),
});
```
how to call the action from a page:
```
const result = await addIndex({ urls: ["https://example.com", "https://example.org"] });
```
Performance Optimization:
- Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC)
- Wrap client components in Suspense with fallback
- Use dynamic loading for non-critical components
- Optimize images: use WebP format, include size data, implement lazy loading
Key Conventions:
- Use 'nuqs' for URL search parameter state management
- Optimize Web Vitals (LCP, CLS, FID)
- Limit 'use client':
- Favor server components and Next.js SSR
- Use only for Web API access in small components
- Avoid for data fetching or state management
Follow Next.js docs for Data Fetching, Rendering, and Routing