Forms

Learn how to build forms in the Next.js Supabase kit

Now that we have created a custom hook to write data to Firestore, we need to build a form to create a new task.

The example below is straightforward:

  1. We have a form with two input fields
  2. When the form is submitted, we read the data using FormData
  3. Finally, we add the document using the callback returned by the hook useCreateTask
components/tasks/CreateTaskForm.tsx
'use client';
 
import type { FormEventHandler } from 'react';
import { useCallback } from 'react';
import { useRouter } from 'next/navigation';
import toaster from 'react-hot-toast';
 
import TextField from '~/core/ui/TextField';
import Button from '~/core/ui/Button';
import If from '~/core/ui/If';
 
import useCreateTaskMutation from '~/lib/tasks/hooks/use-create-task';
import useCurrentOrganization from '~/lib/organizations/hooks/use-current-organization';
 
const CreateTaskForm = () => {
  const { trigger, isMutating } = useCreateTaskMutation();
  const router = useRouter();
  const organization = useCurrentOrganization();
  const organizationId = organization?.id as number;
 
  const onCreateTask: FormEventHandler<HTMLFormElement> = useCallback(
    async (event) => {
      event.preventDefault();
 
      const target = event.currentTarget;
      const data = new FormData(target);
      const name = data.get('name') as string;
      const dueDate = (data.get('dueDate') as string) || getDefaultDueDate();
 
      if (name.trim().length < 3) {
        toaster.error('Task name must be at least 3 characters long');
 
        return;
      }
 
      const task = {
        organizationId,
        name,
        dueDate,
        done: false,
      };
 
      // create task
      await trigger(task);
 
      // redirect to /tasks
      return router.push(`/tasks`);
    },
    [router, trigger, organizationId]
  );
 
  return (
    <form onSubmit={onCreateTask}>
      <div className={'flex flex-col space-y-2'}>
        <TextField.Label>
          Name
          <TextField.Input
            required
            name={'name'}
            placeholder={'ex. Launch on IndieHackers'}
          />
          <TextField.Hint>Hint: whatever you do, ship!</TextField.Hint>
        </TextField.Label>
 
        <TextField.Label>
          Due date
          <TextField.Input name={'dueDate'} type={'date'} />
        </TextField.Label>
 
        <div
          className={
            'flex flex-col space-y-2 md:flex-row md:space-y-0 md:space-x-2'
          }
        >
          <Button loading={isMutating}>
            <If
              condition={isMutating}
              fallback={<>Create Task</>}
            >
              Creating Task...
            </If>
          </Button>
 
          <Button color={'transparent'} href={'/tasks'}>
            Go back
          </Button>
        </div>
      </div>
    </form>
  );
};
 
function getDefaultDueDate() {
  const date = new Date();
  date.setDate(date.getDate() + 1);
  date.setHours(23, 59, 59);
 
  return date.toDateString();
}
 
export default CreateTaskForm;

Subscribe to our Newsletter
Get the latest updates about React, Remix, Next.js, Firebase, Supabase and Tailwind CSS