MonoStack
Concepts

Single File

Upload, replace, and edit a single file attached to a record.

Schema

Use zaf to bind FileInput.Root to a file field:

import { FileInput } from '@repo/storage'
import { zaf } from '@repo/form'
import { z } from 'zod'

export const myFormSchema = z.object({
  name: z.string(),
  file: zaf(z.file(), {
    component: FileInput.Root,
  }),
})

Create

Upload the file and store the returned createdFile.id as a foreign key:

export const createForm = form(myFormSchema, async ({ file, ...data }) => {
  await serverFileManager.createBucket('my-bucket')
  const { createdFile } = await serverFileManager.sync('my-bucket', { file })
  await db.insert(myTable).values({ ...data, fileId: createdFile.id })
})

Update

Pass oldFileId to replace the previous file. The old file is deleted from both S3 and the database:

export const updateForm = form(myFormSchema, async ({ file, ...data }) => {
  if (file) {
    const oldFileId = await db.select({ fileId }).from(myTable).where(...).then(r => r[0]?.fileId)
    const { createdFile } = await serverFileManager.sync('my-bucket', { file, oldFileId })
    // createdFile replaces old file
  }
})

Editing -- FileInput.Provider

Wrap the form in FileInput.Provider to show the existing file in the input. This enables preview and replacement:

<FileInput.Provider fileMeta={data.existingFile}>
  <TypedForm.Remote form={updateForm} enctype="multipart/form-data" />
</FileInput.Provider>

fileMeta expects an object with name, mimeType, size, key, and bucket.

On this page