nextjs-cms

documentField

Upload a document such as a PDF, a Word or Excel file, a presentation, or a CSV.

import { documentField } from 'nextjs-cms/core/fields'

Basic usage

documentField({ name: 'brochure' })

Accepts a PDF up to 2 MB.

With every option

documentField({
    name: 'spec_sheet',
    label: { en: 'Specification sheet', ar: 'ورقة المواصفات' },
    required: true,
    order: 8,
    localized: true, // a different PDF per language
    conditionalRules: [{ field: hasSpecs, condition: 'equals', value: true }],

    fileType: ['pdf', 'docx', 'xlsx'],
    maxFileSize: { size: 20, unit: 'mb' },
})

Options

OptionTypeDefaultDescription
fileType('pdf' | 'doc' | 'docx' | 'xls' | 'xlsx' | 'ppt' | 'pptx' | 'txt' | 'csv')[]['pdf']Accepted formats.
maxFileSize{ size: number; unit: 'kb' | 'mb' }{ size: 2, unit: 'mb' }Largest accepted upload.

Plus the common options.

Validation

  • The extension must be in fileType, and for binary formats the file's content must match it (magic bytes), so a renamed executable is rejected.
  • The size must be within maxFileSize.

Examples

Downloadable price list

documentField({ name: 'price_list', fileType: ['pdf', 'xlsx'], maxFileSize: { size: 10, unit: 'mb' } })
documentField({ name: 'terms_pdf', label: 'Terms (PDF)', fileType: ['pdf'], localized: true })

Each locale gets its own file; see Localization.

Files are stored without an extension, so give the browser a proper name and type when serving them:

website/app/downloads/[id]/route.ts
import { readFile } from 'node:fs/promises'
import path from 'node:path'

export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
    const product = await getProduct(Number((await params).id))
    if (!product?.specSheet) return new Response('Not found', { status: 404 })

    const file = await readFile(path.join(process.env.CMS_UPLOADS_DIR!, '.documents', 'products', product.specSheet))
    return new Response(file, {
        headers: {
            'Content-Type': 'application/pdf',
            'Content-Disposition': `attachment; filename="${product.slug}-specs.pdf"`,
        },
    })
}

Storage

ColumnVARCHAR(50)
ValueGenerated file name (21 hex characters, no extension)
File<uploads>/.documents/<section>/<name>