@fnd-platform/frontend
v1.0.0-alpha.16
Published
Projen project class for generating Remix frontend packages in fnd-platform
Downloads
114
Maintainers
Readme
@fnd-platform/frontend
Projen project class for generating Remix frontend applications with Vite, Tailwind CSS, and shadcn/ui in fnd-platform monorepos.
Installation
npm install -D @fnd-platform/frontend
# or
pnpm add -D @fnd-platform/frontendQuick Start
Add a frontend package to your monorepo in .projenrc.ts:
import { FndMonorepoProject } from '@fnd-platform/core';
import { FndApiProject } from '@fnd-platform/api';
import { FndFrontendProject } from '@fnd-platform/frontend';
const monorepo = new FndMonorepoProject({
name: 'my-app',
defaultReleaseBranch: 'main',
});
const api = new FndApiProject({
parent: monorepo,
name: 'api',
outdir: 'packages/api',
});
const frontend = new FndFrontendProject({
parent: monorepo,
name: 'frontend',
outdir: 'packages/frontend',
api: api, // Link to API for type-safe client
auth: true,
port: 3000,
shadcnTheme: 'zinc',
});
monorepo.synth();Configuration Options
| Option | Type | Default | Description |
| ------------- | -------------------- | ----------------- | ----------------------------------------------- |
| parent | FndMonorepoProject | required | Parent monorepo |
| name | string | required | Package name |
| api | FndApiProject | required | Linked API project |
| outdir | string | packages/{name} | Output directory |
| auth | boolean | true | Enable Cognito authentication |
| port | number | 3000 | Development server port |
| shadcnTheme | ShadcnTheme | 'zinc' | Theme color (zinc, slate, stone, gray, neutral) |
What Gets Generated
packages/frontend/
├── app/
│ ├── routes/
│ │ ├── _index.tsx # Home page
│ │ ├── _auth.tsx # Auth layout
│ │ ├── _auth.login.tsx # Login page
│ │ ├── _auth.signup.tsx # Signup page
│ │ └── dashboard.tsx # Protected dashboard
│ ├── components/
│ │ ├── ui/ # shadcn/ui components
│ │ └── layout/ # Layout components
│ ├── lib/
│ │ ├── api.server.ts # Server-side API client
│ │ ├── auth.server.ts # Auth utilities
│ │ └── session.server.ts # Session management
│ ├── styles/
│ │ └── globals.css # Global styles
│ └── root.tsx # Root component
├── public/
│ └── favicon.ico
├── package.json
├── tailwind.config.js
├── components.json # shadcn/ui config
├── vite.config.ts
├── tsconfig.json
└── vitest.config.tsExamples
Route with Data Loading
// app/routes/blog.$slug.tsx
import { json, type LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { contentApi } from '~/lib/api.server';
export const loader = async ({ params }: LoaderFunctionArgs) => {
const post = await contentApi.getBySlug(params.slug!);
if (!post) {
throw new Response('Not Found', { status: 404 });
}
return json({ post });
};
export default function BlogPost() {
const { post } = useLoaderData<typeof loader>();
return (
<article className="prose max-w-none">
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}Protected Route
// app/routes/dashboard.tsx
import { json, type LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { requireAuth } from '~/lib/auth.server';
export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuth(request);
return json({ user });
};
export default function Dashboard() {
const { user } = useLoaderData<typeof loader>();
return (
<div>
<h1>Welcome, {user.name}</h1>
</div>
);
}Form with Action
// app/routes/contact.tsx
import { json, redirect, type ActionFunctionArgs } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { Button } from '~/components/ui/button';
import { Input } from '~/components/ui/input';
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const email = formData.get('email') as string;
const message = formData.get('message') as string;
// Validate and send
if (!email || !message) {
return json({ error: 'All fields required' }, { status: 400 });
}
await sendContactForm({ email, message });
return redirect('/thank-you');
};
export default function Contact() {
const actionData = useActionData<typeof action>();
return (
<Form method="post" className="space-y-4">
{actionData?.error && (
<p className="text-red-500">{actionData.error}</p>
)}
<Input name="email" type="email" placeholder="Email" />
<Input name="message" placeholder="Message" />
<Button type="submit">Send</Button>
</Form>
);
}Development
# Start development server
cd packages/frontend
pnpm dev
# Build for production
pnpm build
# Run tests
pnpm test
# Type check
pnpm typecheckAdding shadcn/ui Components
cd packages/frontend
npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add dialog
npx shadcn@latest add dropdown-menuAPI Reference
See the full API documentation for detailed type definitions and examples.
Project Class
FndFrontendProject- Projen project class for frontend packages
Types
import type { FndFrontendProjectOptions, ShadcnTheme } from '@fnd-platform/frontend';
// ShadcnTheme = 'zinc' | 'slate' | 'stone' | 'gray' | 'neutral'Requirements
- Node.js 20+
- pnpm 8+
- @fnd-platform/core
- @fnd-platform/api (for API linking)
Related
- @fnd-platform/core - Core Projen project classes
- @fnd-platform/api - API package for backend
- @fnd-platform/cognito-auth - Authentication utilities
- shadcn/ui Documentation
- Remix Documentation
License
MIT
