Add File Upload Support

Open
#36 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
30/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
react, typescript
Domain
frontend

Research direction

Start by locating the existing useStacValue hook and its current URL-fetching path; the issue does not name source files or test paths. Compare the proposed file, URL, and JSON inputs, then add focused coverage for loading, invalid files, validation, concurrent inputs, and file sizes, with documentation for upload and drag-and-drop usage.

Written by the indexing model from the issue text.

Description

Problem

stac-react currently only supports fetching STAC resources from URLs. There's no support for:

  • Loading STAC JSON from uploaded files
  • Exploring local STAC catalogs
  • Working with STAC files without hosting them
  • Development and testing with local files

Projects like stac-map need to allow users to drag-and-drop or upload STAC JSON files for immediate visualization without requiring the files to be hosted.

Current Behavior

// Only URL-based fetching is supported
const { collection } = useCollection('collection-id'); // Requires API
const { item } = useItem('https://example.com/item.json'); // Requires URL

No way to load from File objects or FileList.

Desired Behavior

import { useStacValue } from '@developmentseed/stac-react';

function StacFileUploader() {
  const [file, setFile] = useState<File | null>(null);
  
  const { value, isLoading, error } = useStacValue({
    file: file,
  });
  
  return (
    <div>
      <input 
        type="file" 
        accept=".json,application/json"
        onChange={(e) => setFile(e.target.files?.[0])}
      />
      {value && <StacViewer value={value} />}
    </div>
  );
}

Use Cases from stac-map

stac-map supports file uploads via:

const fileUpload = useFileUpload({ maxFiles: 1 });

const { value, error } = useStacValue({
  href,
  fileUpload, // Handles uploaded files
});

// In the UI:
<FileUpload.Dropzone>
  <Map value={value} />
</FileUpload.Dropzone>

Proposed Solution

Enhanced Hook Signature
type UseStacValueOptions = {
  // URL-based
  url?: string;
  
  // File-based
  file?: File;
  
  // Raw JSON
  json?: object;
  
  // Fetch options (for URL)
  headers?: Record<string, string>;
  method?: 'GET' | 'POST';
  body?: unknown;
};

function useStacValue(options: UseStacValueOptions): UseStacValueResult;
Implementation
import { useQuery } from '@tanstack/react-query';

function useStacValue(options: UseStacValueOptions): UseStacValueResult {
  const { url, file, json, headers, method, body } = options;
  
  const { data, error, isLoading, isFetching, refetch } = useQuery({
    queryKey: ['stac-value', url, file?.name, json, method],
    queryFn: async () => {
      let value: any;
      
      if (json) {
        // Direct JSON object
        value = json;
      } else if (file) {
        // File upload
        const text = await file.text();
        value = JSON.parse(text);
      } else if (url) {
        // URL fetch
        const response = await fetch(url, {
          method: method || 'GET',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            ...headers,
          },
          body: body ? JSON.stringify(body) : undefined,
        });
        
        if (!response.ok) {
          throw new ApiError(
            response.statusText,
            response.status,
            await response.text(),
            response.url
          );
        }
        
        value = await response.json();
      } else {
        throw new Error('Must provide url, file, or json');
      }
      
      return {
        value,
        type: determineStacType(value),
      };
    },
    enabled: !!(url || file || json),
    retry: false,
  });

  return {
    value: data?.value,
    type: data?.type,
    isLoading,
    isFetching,
    error,
    refetch,
  };
}
Example Usage Patterns
1. File Input
function FileUploadExample() {
  const [file, setFile] = useState<File | null>(null);
  const { value, isLoading, error } = useStacValue({ file });
  
  return (
    <div>
      <input 
        type="file" 
        onChange={(e) => setFile(e.target.files?.[0] || null)}
      />
      {isLoading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}
      {value && <pre>{JSON.stringify(value, null, 2)}</pre>}
    </div>
  );
}
2. Drag and Drop
function DragDropExample() {
  const [file, setFile] = useState<File | null>(null);
  const { value } = useStacValue({ file });
  
  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    const droppedFile = e.dataTransfer.files[0];
    if (droppedFile?.type === 'application/json') {
      setFile(droppedFile);
    }
  };
  
  return (
    <div 
      onDrop={handleDrop}
      onDragOver={(e) => e.preventDefault()}
    >
      Drop STAC JSON file here
      {value && <StacViewer value={value} />}
    </div>
  );
}
3. Combined URL and File
function FlexibleLoader({ url, file }) {
  // Automatically uses file if provided, otherwise URL
  const { value, isLoading } = useStacValue({ 
    url: !file ? url : undefined,
    file: file,
  });
  
  return <div>{/* ... */}</div>;
}

File Validation

Add optional validation:

function validateStacFile(file: File): Promise<boolean> {
  return new Promise((resolve) => {
    if (!file.name.endsWith('.json')) {
      resolve(false);
      return;
    }
    
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const json = JSON.parse(e.target?.result as string);
        // Check for STAC required fields
        resolve(
          json.type && 
          json.stac_version &&
          json.links
        );
      } catch {
        resolve(false);
      }
    };
    reader.readAsText(file);
  });
}

Benefits

  • ✅ Load STAC from uploaded files
  • ✅ Support drag-and-drop workflows
  • ✅ Enable offline/local STAC exploration
  • ✅ Simplify development and testing
  • ✅ Support in-memory STAC objects
  • ✅ Maintain URL-based fetching

Breaking Changes

None - this extends existing functionality with backward compatibility.

Testing Requirements

  • Test loading from File objects
  • Test loading from URL
  • Test loading from JSON objects
  • Test error handling for invalid files
  • Test file validation
  • Test with different file sizes
  • Test concurrent file and URL loading

Documentation Requirements

  • Document file upload patterns
  • Provide drag-and-drop examples
  • Document file validation
  • Show integration with file input libraries
  • Document security considerations
Dominant language
TypeScript
Stars
34
Forks
4
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from developmentseed/stac-react

All issues in developmentseed/stac-react

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.