Binary files

Working with Files

Files in Val

Val supports generic file uploads for any file type, including videos, PDFs, audio files, and more. Files work similarly to images, but with more flexibility in file types.

Key Differences from Images

The main differences when working with files instead of images:

  • Use s.file() instead of s.image() in your schema

  • Use c.file() instead of c.image() for local files

  • You can specify accepted file types with the accept option (e.g., accept: 'video/*', accept: '.pdf,.doc')

  • When you use accept: 'video/*', Val Studio will render it as a video with preview

Schema and Content

const mediaSchema = s.object({
  video: s.file({ accept: "video/*" }),
  document: s.file({ accept: ".pdf,.doc,.docx" }),
  audio: s.file({ accept: "audio/*" }),
  anyFile: s.file(), // Accepts any file type
});

export default c.define("/content/media.val.ts", mediaSchema, {
  video: c.file("/public/val/video.mp4", {
    mimeType: "video/mp4"
  }),
  document: c.file("/public/val/guide.pdf", {
    mimeType: "application/pdf"
  }),
  audio: c.file("/public/val/podcast.mp3", {
    mimeType: "audio/mpeg"
  }),
  anyFile: c.file("/public/val/data.json", {
    mimeType: "application/json"
  }),
});

Using Files in Components

When using files in your components, access the URL using the .url property:

Basic usage

import { fetchVal } from "@/val/val.rsc";
import mediaVal from "./media.val";

export default async function MediaPage() {
  const { video, document, audio } = await fetchVal(mediaVal);
  
  return (
    <div>
      <video src={video.url} controls className="w-full" />
      
      <a href={document.url} download className="btn">
        Download PDF
      </a>
      
      <audio src={audio.url} controls />
    </div>
  );
}

Using val.attrs and val.raw

For advanced use cases where you need to control visual editing attributes, you can use val.attrs() and val.raw() with the file URL:

import { val } from "@/val.config";
import { fetchVal } from "@/val/val.rsc";
import mediaVal from "./media.val";

export default async function MediaPage() {
  const { video } = await fetchVal(mediaVal);
  
  return (
    <video
      controls
      className="w-full"
      {...val.attrs(video.url)}
    >
      <source src={val.raw(video.url)} type="video/mp4" />
      Your browser does not support the video tag.
    </video>
  );
}

More Information

For detailed information about adding metadata, working with remote files, and using the CLI or VS Code extension, see the Images Guide. The workflow is identical - just use s.file() and c.file() instead of s.image() and c.image().

For a comprehensive guide on working specifically with videos, see the Videos Guide.