Attachments

free

Attachments presents removable file chips or image tiles with reusable preview and file-info parts.

Preview
Dark
launch-brief.png
launch-brief.png
image/png
PDF
notes.pdf
application/pdf
import { useState } from 'react';
import Attachments, { Attachment } from '@/components/composites/Attachments';

const initialFiles = [
  {
    id: 'brief',
    name: 'launch-brief.png',
    mediaType: 'image/png',
    url: '/img/thumbs/misc/img-1.png',
  },
  {
    id: 'notes',
    name: 'notes.pdf',
    mediaType: 'application/pdf',
    url: '/notes.pdf',
  },
];

export default function UsageDemo() {
  const [files, setFiles] = useState(initialFiles);

  return (
    <Attachments>
      {files.map((file) => (
        <Attachment
          key={file.id}
          data={file}
          onRemove={() =>
            setFiles((current) => current.filter((item) => item.id !== file.id))
          }
        >
          <Attachment.Preview />
          <Attachment.Info />
        </Attachment>
      ))}
    </Attachments>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Attachments

Examples

Grid

Preview
Dark
reference-1.png
reference-2.png
reference-3.png
import { useState } from 'react';
import Attachments, { Attachment } from '@/components/composites/Attachments';

const images = [
  { id: 'one', name: 'reference-1.png', mediaType: 'image/png', url: '/img/thumbs/misc/img-1.png' },
  { id: 'two', name: 'reference-2.png', mediaType: 'image/png', url: '/img/thumbs/misc/img-2.png' },
  { id: 'three', name: 'reference-3.png', mediaType: 'image/png', url: '/img/thumbs/misc/img-3.png' },
];

export default function GridDemo() {
  const [items, setItems] = useState(images);

  return (
    <div className="max-w-xs">
      <Attachments variant="grid">
        {items.map((image) => (
          <Attachment
            key={image.id}
            data={image}
            variant="grid"
            onRemove={() =>
              setItems((current) => current.filter((item) => item.id !== image.id))
            }
          >
            <Attachment.Preview />
          </Attachment>
        ))}
      </Attachments>
    </div>
  );
}

Non-image Files

Preview
Dark
PDF
scope.pdf
application/pdf
XLS
estimates.xlsx
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
import Attachments, { Attachment } from '@/components/composites/Attachments';

const files = [
  { id: 'pdf', name: 'scope.pdf', mediaType: 'application/pdf', url: '/scope.pdf' },
  { id: 'sheet', name: 'estimates.xlsx', mediaType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', url: '/estimates.xlsx' },
];

export default function NonImageFilesDemo() {
  return (
    <Attachments>
      {files.map((file) => (
        <Attachment key={file.id} data={file}>
          <Attachment.Preview />
          <Attachment.Info />
        </Attachment>
      ))}
    </Attachments>
  );
}

API

Attachments

PropDescriptionTypeDefault
variantArranges chips in a wrapping row, square tiles in a grid, or images at their own size.'inline' | 'grid' | 'media''inline'
...divPropsStandard <div> attributes forwarded to the container.ComponentProps<'div'>

Attachment

PropDescriptionTypeDefault
dataFile metadata and preview URL.AttachmentData
onRemoveMakes the remove action available.(attachment: AttachmentData) => void
variantMatches the parent layout. Inherited from Attachments when omitted.'inline' | 'grid' | 'media''inline'
childrenUse Preview and Info to arrange the item explicitly.ReactNode

Types

type AttachmentData = {
  id: string;
  name: string;
  mediaType: string;
  url: string;
};