Skip to content

Instantly share code, notes, and snippets.

@donnes
Last active November 20, 2023 08:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donnes/38ca81f3c0d7c1e81dd7c4f883268d28 to your computer and use it in GitHub Desktop.
Save donnes/38ca81f3c0d7c1e81dd7c4f883268d28 to your computer and use it in GitHub Desktop.
Uploadthing custom UploadDropzone implementation
import { useState } from 'react'
import { UploadDropzone } from "~/components/upload-dropzone";
import { useUploadThing } from "~/hooks/use-uploadthing";
import type { FileRouter } from "~/server/uploadthing";
const Home: NextPage = () => {
const [files, setFiles] = useState<File[]>([]);
const { startUpload, isUploading, permittedFileInfo } = useUploadThing({
endpoint: "upload",
onClientUploadComplete: () => console.log("Upload completed"),
onUploadError: () => console.log("Something went wrong"),
});
return (
<>
<Head>
<title>How to Use</title>
</Head>
<UploadDropzone
onChange={(acceptedFiles) => setFiles(acceptedFiles)}
permittedFileInfo={permittedFileInfo}
/>
{Boolean(files.length) && (
<button
type="button"
onClick={() => {
if (!files) return;
void startUpload(files);
}}
disabled={isUploading}
>
{isUploading ? 'Uploading...' : 'Upload'}
</button>
)}
</>
);
};
export default Home;
import { useCallback } from "react";
import { type FileWithPath, useDropzone } from "react-dropzone";
import { classNames, generateClientDropzoneAccept } from "uploadthing/client";
import { UploadCloud } from "lucide-react";
import { Button } from "~/components/ui/button";
export function UploadDropzone(props: {
onChange?: (files: File[]) => void;
maxFiles?: number;
permittedFileInfo?: {
maxSize: string;
fileTypes: string[];
};
className?: string;
}) {
const onDrop = useCallback(
(acceptedFiles: FileWithPath[]) => {
props.onChange?.(acceptedFiles);
},
[props]
);
const { maxSize, fileTypes } = props.permittedFileInfo ?? {};
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: fileTypes ? generateClientDropzoneAccept(fileTypes) : undefined,
maxFiles: props.maxFiles ?? 1,
});
return (
<div className={classNames("min-h-[450px]", props?.className ?? "")}>
<label
htmlFor="file-upload"
className={classNames(
"flex h-full w-full cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed border-violet-300",
"bg-violet-50 text-primary/80 hover:bg-violet-100 hover:text-primary",
isDragActive ? "border-sky-600 bg-sky-50 hover:bg-sky-100" : "",
)}
{...getRootProps()}
>
<div className="flex flex-col items-center justify-center pb-6 pt-5">
<UploadCloud className="h-14 w-14" />
<p className="mb-2 cursor-pointer text-center text-base">
To get started,{" "}
<span className="font-semibold">click to upload</span>
<br />
or <span className="font-semibold">drag and drop</span> here
</p>
<div className="h-[1.25rem]">
{fileTypes && (
<p className="text-sm leading-5 text-primary/60">
<span className="capitalize">{`${fileTypes.join(", ")}`}</span>{" "}
{maxSize && `up to ${maxSize}`}
</p>
)}
</div>
</div>
<input className="sr-only" {...getInputProps()} />
</label>
</div>
);
}
import * as React from 'react';
import { Trash } from "lucide-react";
import { classNames } from "uploadthing/client";
import { Button } from "~/components/ui/button";
export function UploadItem(props: {
file: File;
onRemove?: (file: File) => void;
className?: string;
}) {
const { file, onRemove } = props;
return (
<div
className={classNames(
"relative h-40 w-full overflow-hidden rounded-md shadow-lg",
props?.className ?? ""
)}
>
<Button
type="button"
className="absolute right-2 top-2 z-10 h-6 w-6 p-0"
onClick={() => onRemove?.(file)}
aria-label="Remove file"
variant="secondary"
size="sm"
>
<Trash className="h-4 w-4 text-destructive" />
</Button>
{file.type.startsWith("video/") && (
<video
key={file.name}
src={URL.createObjectURL(file)}
className="pointer-events-none h-full w-full object-cover"
controls={false}
/>
)}
{file.type.startsWith("image/") && (
<Image
src={URL.createObjectURL(file)}
alt={file.name}
width={200}
height={200}
className="pointer-events-none h-full w-full object-cover"
/>
)}
<div className="absolute inset-0 flex items-end bg-gradient-to-t from-slate-950/60 to-transparent">
<span className="p-2 text-sm font-semibold text-white">
{file.name}
</span>
</div>
</div>
);
}
import { generateReactHelpers } from "@uploadthing/react/hooks";
import type { FileRouter } from "~/server/uploadthing";
const { useUploadThing } = generateReactHelpers<FileRouter>();
export { useUploadThing };
@Ezejaemmanuel
Copy link

awesome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment