Skip to main content
For Feria Nounish Artists: This documentation is inspired by Gabriel Lago from Cali, Colombia, who is building a Feria Nounish MiniApp for the artists participating in this year’s fair. The uploadToArweave function enables artists to upload their artwork and metadata to decentralized storage, creating permanent, onchain records of their contributions to the collective timeline.
Upload files to Arweave decentralized storage for use in your NFT collections and moments. This function works on both client-side and server-side environments, making it suitable for browser-based uploads as well as backend services and API routes.
Note: This function uploads files to Arweave and returns a URI that can be used in collection creation or moment creation.

Installation

First, install the required Arweave package:
npm install arweave

Function Implementation

This guide uses the In•Process Arweave Proxy API to upload chunks to Arweave. The proxy forwards requests to https://arweave.net, so your client never connects directly to the Arweave gateway.

postToArweave

A helper that sends transaction headers and chunks through the proxy:
const IN_PROCESS_API = "https://api.inprocess.world/api";

const postToArweave = async (path: string, body: unknown): Promise<Response> => {
  const res = await fetch(`${IN_PROCESS_API}/arweave/${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/json", Accept: "text/plain" },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    throw new Error(`Arweave ${path} request failed: ${res.status}`);
  }
  return res;
};

export default postToArweave;

uploadToArweave

The main upload function that signs a transaction locally, then uploads the header and each chunk via the proxy:
import Arweave from "arweave";
import postToArweave from "./postToArweave";

const arweave = Arweave.init({
  host: "arweave.net",
  port: 443,
  protocol: "https",
  timeout: 20000,
  logging: false,
});

const uploadToArweave = async (
  file: File,
  getProgress: (progress: number) => void = () => {}
): Promise<string> => {
  const ARWEAVE_KEY = JSON.parse(
    Buffer.from(
      process.env.NEXT_PUBLIC_ARWEAVE_KEY as string,
      "base64"
    ).toString()
  );
  const buffer = await file.arrayBuffer();

  const transaction = await arweave.createTransaction(
    {
      data: buffer,
    },
    ARWEAVE_KEY
  );
  transaction.addTag("Content-Type", file.type);
  await arweave.transactions.sign(transaction, ARWEAVE_KEY);

  const data = transaction.data;
  const totalChunks = transaction.chunks!.chunks.length;

  // Post transaction header via proxy (with data emptied for chunked upload)
  transaction.data = new Uint8Array(0);
  await postToArweave("tx", transaction);

  // Upload each chunk via proxy
  for (let i = 0; i < totalChunks; i++) {
    const chunk = transaction.getChunk(i, data);
    await postToArweave("chunk", chunk);
    const pctComplete = Math.round(((i + 1) / totalChunks) * 100);
    console.log(`${pctComplete}% complete, ${i + 1}/${totalChunks}`);
    getProgress(pctComplete);
  }

  return `ar://${transaction.id}`;
};

export default uploadToArweave;

Parameters

ParameterTypeRequiredDescription
fileFileYesThe file to upload to Arweave
getProgress(progress: number) => voidNoOptional callback function to track upload progress (0-100)

Return Value

Returns a Promise that resolves to a string containing the Arweave URI in the format: ar://{transaction_id}

Environment Setup

You’ll need to set up your Arweave wallet key as an environment variable:
# Your Arweave wallet key (base64 encoded)
NEXT_PUBLIC_ARWEAVE_KEY=your_base64_encoded_arweave_key_here

Usage Examples

Basic Upload

import uploadToArweave from "@/lib/arweave/uploadToArweave";

// Upload a file
const file = document.getElementById("fileInput").files[0];
const arweaveUri = await uploadToArweave(file);
console.log("File uploaded to:", arweaveUri);
// Output: ar://abc123...

Upload with Progress Tracking

import uploadToArweave from "@/lib/arweave/uploadToArweave";

const file = document.getElementById("fileInput").files[0];

// Track upload progress
const handleProgress = (progress: number) => {
  console.log(`Upload progress: ${progress}%`);
  // Update your UI progress bar
  document.getElementById("progressBar").style.width = `${progress}%`;
};

try {
  const arweaveUri = await uploadToArweave(file, handleProgress);
  console.log("Upload complete:", arweaveUri);
} catch (error) {
  console.error("Upload failed:", error);
}

Upload for Collection Metadata

import uploadToArweave from "@/lib/arweave/uploadToArweave";

// Upload collection metadata JSON
const metadata = {
  name: "Feria Nounish Collection",
  description: "Artwork from the Feria Nounish fair",
  image: "https://example.com/collection-image.jpg",
  external_link: "https://ferianounish.com",
};

// Convert metadata to file
const metadataBlob = new Blob([JSON.stringify(metadata, null, 2)], {
  type: "application/json",
});
const metadataFile = new File([metadataBlob], "metadata.json", {
  type: "application/json",
});

// Upload to Arweave
const contractUri = await uploadToArweave(metadataFile);
console.log("Contract URI:", contractUri);
// Use this URI in your collection creation

Upload for Moment Content

import uploadToArweave from "@/lib/arweave/uploadToArweave";

// Upload moment content (text, image, etc.)
const momentContent = {
  title: "My Feria Moment",
  content: "This is my contribution to the collective timeline",
  media: "https://example.com/my-artwork.jpg",
  timestamp: new Date().toISOString(),
};

const contentBlob = new Blob([JSON.stringify(momentContent, null, 2)], {
  type: "application/json",
});
const contentFile = new File([contentBlob], "moment.json", {
  type: "application/json",
});

const momentUri = await uploadToArweave(contentFile);
console.log("Moment URI:", momentUri);
// Use this URI when creating your moment

React Hook Example

For React applications, here’s a custom hook for file uploads:
import { useState } from "react";
import uploadToArweave from "@/lib/arweave/uploadToArweave";

export const useArweaveUpload = () => {
  const [isUploading, setIsUploading] = useState(false);
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState<string | null>(null);

  const uploadFile = async (file: File): Promise<string> => {
    setIsUploading(true);
    setProgress(0);
    setError(null);

    try {
      const uri = await uploadToArweave(file, setProgress);
      setIsUploading(false);
      return uri;
    } catch (err) {
      setError(err instanceof Error ? err.message : "Upload failed");
      setIsUploading(false);
      throw err;
    }
  };

  return {
    uploadFile,
    isUploading,
    progress,
    error,
  };
};

// Usage in component
const MyComponent = () => {
  const { uploadFile, isUploading, progress, error } = useArweaveUpload();

  const handleFileUpload = async (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const file = event.target.files?.[0];
    if (file) {
      try {
        const uri = await uploadFile(file);
        console.log("Uploaded to:", uri);
      } catch (err) {
        console.error("Upload failed:", err);
      }
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileUpload} />
      {isUploading && (
        <div>
          <p>Uploading... {progress}%</p>
          <progress value={progress} max={100} />
        </div>
      )}
      {error && <p style={{ color: "red" }}>Error: {error}</p>}
    </div>
  );
};

File Types Supported

The function supports any file type that can be stored on Arweave:
  • Images: PNG, JPEG, GIF, SVG, WebP
  • Documents: PDF, TXT, MD
  • Data: JSON, CSV, XML
  • Media: MP4, MP3, WebM
  • Archives: ZIP, TAR

Best Practices

  1. File Size: Arweave has no strict size limits, but larger files take longer to upload and cost more AR tokens.
  2. Content-Type Tagging: The function automatically tags files with their MIME type for proper handling.
  3. Progress Tracking: Always implement progress tracking for better user experience, especially for larger files.
  4. Error Handling: Wrap upload calls in try-catch blocks to handle network issues or insufficient AR tokens.
  5. Metadata Structure: When uploading JSON metadata, follow standard NFT metadata schemas for compatibility with marketplaces.

Integration with Collection Creation

After uploading your metadata to Arweave, use the returned URI in your collection creation:
// Upload collection metadata
const contractUri = await uploadToArweave(metadataFile);

// Use in collection creation
const createContractData = encodeFunctionData({
  abi: tokenAbi,
  functionName: "createContract",
  args: [
    contractUri, // Use the Arweave URI here
    collectionName,
    royaltyConfig,
    smartAccount.address,
    [],
  ],
});

Error Handling

Common errors and solutions:
  • Insufficient AR tokens: Ensure your Arweave wallet has enough AR tokens for the upload
  • Invalid wallet key: Verify your NEXT_PUBLIC_ARWEAVE_KEY is correctly base64 encoded
  • Network timeout: Increase the timeout value in the Arweave configuration
  • File too large: Consider compressing or optimizing your files before upload

Next Steps

After uploading files to Arweave: