T
TanStack3mo ago
solid-orange

Form is somehow getting server rendered? RHF

Hi all, Currently porting my Next JS app to Start but running into issues with my form component. When I console log form, it get's logged in the terminal. I'm assuming this may have to do with how my route is set up, but I'm not sure how to adjust. I appreciate any advice. route file
import { createFileRoute } from '@tanstack/react-router';
import CreateAnAccountForm from '../../components/feat/accounts/create-an-account/create-an-account-form';

export const Route = createFileRoute('/onboarding/')({
component: RouteComponent,
});

function RouteComponent() {
return (
<div className="flex min-h-screen w-full flex-col items-center justify-center p-4">
<div className="mx-auto w-full max-w-md">
<CreateAnAccountForm />
</div>
</div>
);
}
import { createFileRoute } from '@tanstack/react-router';
import CreateAnAccountForm from '../../components/feat/accounts/create-an-account/create-an-account-form';

export const Route = createFileRoute('/onboarding/')({
component: RouteComponent,
});

function RouteComponent() {
return (
<div className="flex min-h-screen w-full flex-col items-center justify-center p-4">
<div className="mx-auto w-full max-w-md">
<CreateAnAccountForm />
</div>
</div>
);
}
4 Replies
solid-orange
solid-orangeOP3mo ago
Form component file
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import z from 'zod';
import { useState } from 'react';
import { Button } from '@workspace/ui/components/button';
import { Form } from '@workspace/ui/components/form/form';
import {
TextInput,
RadioInputGroup,
} from '@workspace/ui/components/form/inputs';
import { InputsWrapper } from '@workspace/ui/components/form/wrappers';

import { useUser } from '@clerk/tanstack-react-start';
import { useMutation } from 'convex/react';
import { api } from '../../../../../convex/_generated/api';
import { useRouter } from '@tanstack/react-router';

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@workspace/ui/components/card';

const INTEREST_OPTIONS = [
{ value: 'vendor', label: '出店者になりたい' },
{ value: 'organizer', label: 'マルシェの主催者になりたい' },
{ value: 'both', label: '両方になりたい' },
];

const formSchema = z.object({
name: z.string().min(1, { message: 'マルシェの名前を入力してください' }),
interest: z.enum(
INTEREST_OPTIONS.map((option) => option.value) as [string, ...string[]]
),
});

type CreateAnAccountFormValues = z.infer<typeof formSchema>;
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import z from 'zod';
import { useState } from 'react';
import { Button } from '@workspace/ui/components/button';
import { Form } from '@workspace/ui/components/form/form';
import {
TextInput,
RadioInputGroup,
} from '@workspace/ui/components/form/inputs';
import { InputsWrapper } from '@workspace/ui/components/form/wrappers';

import { useUser } from '@clerk/tanstack-react-start';
import { useMutation } from 'convex/react';
import { api } from '../../../../../convex/_generated/api';
import { useRouter } from '@tanstack/react-router';

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@workspace/ui/components/card';

const INTEREST_OPTIONS = [
{ value: 'vendor', label: '出店者になりたい' },
{ value: 'organizer', label: 'マルシェの主催者になりたい' },
{ value: 'both', label: '両方になりたい' },
];

const formSchema = z.object({
name: z.string().min(1, { message: 'マルシェの名前を入力してください' }),
interest: z.enum(
INTEREST_OPTIONS.map((option) => option.value) as [string, ...string[]]
),
});

type CreateAnAccountFormValues = z.infer<typeof formSchema>;
continued
export default function CreateAnAccountForm() {
const { user } = useUser();

const [isSubmitting, setIsSubmitting] = useState(false);

const form = useForm<CreateAnAccountFormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
name: '',
interest: undefined,
},
});

const createAccount = useMutation(api.accounts.createAccount);
const router = useRouter();

const onSubmit = async (values: CreateAnAccountFormValues) => {
setIsSubmitting(true);

try {
const userId = user?.id;
if (!userId) throw new Error('User not found');

const account = await createAccount({
name: values.name,
creatorUserId: userId,
});

router.navigate({ to: `/a/${account}` });
} catch (err: any) {
console.error(err);
alert(err.message || 'アカウントの作成に失敗しました');
} finally {
setIsSubmitting(false);
}
};

console.log(form);

return (
<div className="flex min-h-screen w-full flex-col items-center justify-center p-4">
<div className="mx-auto w-full max-w-md">
<Card className="w-full">
<CardHeader className="text-center">
<CardTitle className="text-2xl">Marche OSへようこそ</CardTitle>
<CardDescription>
アカウントの設定を完了しましょう。
</CardDescription>
</CardHeader>
<CardContent>
<Form form={form} onSubmit={onSubmit}>
<InputsWrapper>
<TextInput form={form} name="name" label="アカウント名" />
<RadioInputGroup
form={form}
name="interest"
label="興味のある分野"
options={INTEREST_OPTIONS}
variant="vertical"
/>
</InputsWrapper>
export default function CreateAnAccountForm() {
const { user } = useUser();

const [isSubmitting, setIsSubmitting] = useState(false);

const form = useForm<CreateAnAccountFormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
name: '',
interest: undefined,
},
});

const createAccount = useMutation(api.accounts.createAccount);
const router = useRouter();

const onSubmit = async (values: CreateAnAccountFormValues) => {
setIsSubmitting(true);

try {
const userId = user?.id;
if (!userId) throw new Error('User not found');

const account = await createAccount({
name: values.name,
creatorUserId: userId,
});

router.navigate({ to: `/a/${account}` });
} catch (err: any) {
console.error(err);
alert(err.message || 'アカウントの作成に失敗しました');
} finally {
setIsSubmitting(false);
}
};

console.log(form);

return (
<div className="flex min-h-screen w-full flex-col items-center justify-center p-4">
<div className="mx-auto w-full max-w-md">
<Card className="w-full">
<CardHeader className="text-center">
<CardTitle className="text-2xl">Marche OSへようこそ</CardTitle>
<CardDescription>
アカウントの設定を完了しましょう。
</CardDescription>
</CardHeader>
<CardContent>
<Form form={form} onSubmit={onSubmit}>
<InputsWrapper>
<TextInput form={form} name="name" label="アカウント名" />
<RadioInputGroup
form={form}
name="interest"
label="興味のある分野"
options={INTEREST_OPTIONS}
variant="vertical"
/>
</InputsWrapper>
Continued
<Button
type="submit"
className="min-w-32"
size="lg"
disabled={isSubmitting}
>
{isSubmitting ? '送信中...' : 'アカウントを作成'}
</Button>
</Form>
</CardContent>
</Card>
</div>
</div>
);
}
<Button
type="submit"
className="min-w-32"
size="lg"
disabled={isSubmitting}
>
{isSubmitting ? '送信中...' : 'アカウントを作成'}
</Button>
</Form>
</CardContent>
</Card>
</div>
</div>
);
}
quickest-silver
quickest-silver3mo ago
Your code will definitely log once in the terminal because Start is SSR you can call console.log(form); in onSubmit function
solid-orange
solid-orangeOP3mo ago
Currently I can’t even call the onSubmit function and none of the RHF validation is working when I try to submit.
quickest-silver
quickest-silver3mo ago
<Form> component shadcn wrap wrong. You can wrap with <Form {...form} <form onSubmit={onSubmit}> ...

Did you find this page helpful?