ImageDefinition

ImageDefinition is a specialized CoValue designed specifically for managing images in Jazz applications. It extends beyond basic file storage by supporting multiple resolutions of the same image and progressive loading patterns.

Beyond ImageDefinition, Jazz offers higher-level functions and components that make it easier to use images:

The Image Upload example demonstrates use of ProgressiveImg and ImageDefinition.

Creating Images

The easiest way to create and use images in your Jazz application is with the createImage() function:

import { createImage } from "jazz-browser-media-images";

// Create an image from a file input
async function handleFileUpload(event) {
  const file = event.target.files[0];
  if (file) {
    // Creates ImageDefinition with multiple resolutions automatically
    const image = await createImage(file, {
      owner: me.profile._owner,
    });
    
    // Store the image in your application data
    me.profile.image = image;
  }
}

Note: createImage() requires a browser environment as it uses browser APIs to process images.

The createImage() function:

  • Creates an ImageDefinition with the right properties
  • Generates a small placeholder for immediate display
  • Creates multiple resolution variants of your image
  • Returns the created ImageDefinition

Configuration Options

You can configure createImage() with additional options:

// Configuration options
const options = {
  owner: me,                // Owner for access control
  maxSize: 1024             // Maximum resolution to generate
};

// Setting maxSize controls which resolutions are generated:
// 256: Only creates the smallest resolution (256px on longest side)
// 1024: Creates 256px and 1024px resolutions
// 2048: Creates 256px, 1024px, and 2048px resolutions
// undefined: Creates all resolutions including the original size

const image = await createImage(file, options);

Displaying Images with ProgressiveImg

For a complete progressive loading experience, use the ProgressiveImg component:

import { ProgressiveImg } from "jazz-react";

function GalleryView({ image }) {
  return (
    <div className="image-container">
      <ProgressiveImg
        image={image}  // The image definition to load
        targetWidth={800} // Looks for the best available resolution for a 800px image
      >
        {({ src }) => (
          <img 
            src={src} 
            alt="Gallery image" 
            className="gallery-image"
          />
        )}
      </ProgressiveImg>
    </div>
  );
}

The ProgressiveImg component handles:

  • Showing a placeholder while loading
  • Automatically selecting the appropriate resolution
  • Progressive enhancement as higher resolutions become available
  • Cleaning up resources when unmounted

Using useProgressiveImg Hook

For more control over image loading, you can implement your own progressive image component:

import { useProgressiveImg } from "jazz-react";

function CustomImageComponent({ image }) {
  const {
    src,         // Data URI containing the image data as a base64 string,
                 // or a placeholder image URI
    res,         // The current resolution
    originalSize // The original size of the image
  } = useProgressiveImg({
    image: image,  // The image definition to load
    targetWidth: 800  // Limit to resolutions up to 800px wide
  });

  // When image is not available yet
  if (!src) {
    return <div className="image-loading-fallback">Loading image...</div>;
  }
  
  // When image is loading, show a placeholder
  if (res === "placeholder") {
    return <img src={src} alt="Loading..." className="blur-effect" />;
  }

  // Full image display with custom overlay
  return (
    <div className="custom-image-wrapper">
      <img 
        src={src} 
        alt="Custom image" 
        className="custom-image"
      />
      <div className="image-overlay">
        <span className="image-caption">Resolution: {res}</span>
      </div>
    </div>
  );
}

Understanding ImageDefinition

Behind the scenes, ImageDefinition is a specialized CoValue that stores:

  • The original image dimensions (originalSize)
  • An optional placeholder (placeholderDataURL) for immediate display
  • Multiple resolution variants of the same image as FileStreams

Each resolution is stored with a key in the format "widthxheight" (e.g., "1920x1080", "800x450").

// Structure of an ImageDefinition
const image = ImageDefinition.create({
  originalSize: [1920, 1080],
  placeholderDataURL: "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
});

// Accessing the highest available resolution
const highestRes = image.highestResAvailable();
if (highestRes) {
  console.log(`Found resolution: ${highestRes.res}`);
  console.log(`Stream: ${highestRes.stream}`);
}

For more details on using ImageDefinition directly, see the VanillaJS docs.

Fallback Behavior

highestResAvailable returns the largest resolution that fits your constraints. If a resolution has incomplete data, it falls back to the next available lower resolution.

const image = ImageDefinition.create({
  originalSize: [1920, 1080],
});

image["1920x1080"] = FileStream.create(); // Empty image upload
image["800x450"] = await FileStream.createFromBlob(mediumSizeBlob);

const highestRes = image.highestResAvailable();
console.log(highestRes.res); // 800x450