jifile

Utilities about file input for the best UX.

Installation

npm install jifile

Examples

All examples were written using Next.js and Tailwind-css.

Stack Mode Example

You can get files from onFileChange. Even if new files selected, it is not reset and keeps accumulating.

'use client';

import { File, FileInput, FileLabel } from 'jifile';
import ExampleBlock from '../ui/example-block';

function StackModeExamplePreview() {
  const [files, setFiles] = useState<FileList>();
  const fileArray = Array.from(files || []);

  return (
    <>
      <File mode="stack" onFileChange={setFiles} files={files}>
        <FileLabel>Select File</FileLabel>
        <div className="flex flex-col gap-2">
          {fileArray.map((file) => (
            <span key={file.name}>{file.name}</span>
          ))}
        </div>
        <FileInput hidden />
      </File>
    </>
  );
}

Image Preview Example

You can get preveiw image from file easily

'use client'

import { File, FileInput, FileLabel, getPreviewImage } from 'jifile';
import Image from 'next/image';
import { useState } from 'react';

function ImagePreviewExampleResult() {
  const [files, setFiles] = useState<FileList>();
  const fileArray = Array.from(files || []);

  return (
    <>
      <File onFileChange={setFiles}>
        <FileLabel>Select image files</FileLabel>
        <FileInput accept="image/*" hidden multiple />
      </File>
      <div className="flex gap-4">
        {fileArray.map((file) => (
          <Image
            key={file.name}
            width={200}
            height={200}
            src={getPreviewImage(file)}
            alt="image preview"
          />
        ))}
      </div>
    </>
  );
}

With React Hook Form

Simple example with react-hook-form, using stack mode

'use client';

import { File, FileInput, FileLabel, removeFile } from 'jifile';
import { Controller, useForm } from 'react-hook-form';

function WithReactHookFormExampleResult() {
  const { control, watch, setValue, getValues } = useForm<{
    files?: FileList;
  }>();

  const fileArray = Array.from(watch('files') || []);
  return (
    <>
      <Controller
        control={control}
        name={'files'}
        render={({ field }) => (
          <File mode="stack" onFileChange={field.onChange} files={field.value}>
            <FileLabel>Select files</FileLabel>
            <FileInput multiple hidden />
          </File>
        )}
      />
      {fileArray.map((file, index) => (
        <div key={file.name} className="flex gap-2">
          <div>{file.name}</div>
          <button
            className="bg-red-100 size-5 rounded"
            onClick={() => {
              const files = getValues('files');
              if (!files) return;
              const removed = removeFile(files, { index });
              setValue('files', removed);
            }}
          >
            x
          </button>
        </div>
      ))}
    </>
  );
}