This project demonstrates how to upload files to the Arweave permaweb directly from a browser using Next.js 15, ArConnect wallet, and Turbo SDK.
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun devOpen http://localhost:3000 with your browser to see the result.
To build the project, run:
pnpm build
# or
npm run build --no-lint
# or
yarn build --no-lintThis project features a fully client-side implementation for uploading files to the Arweave network directly from the browser using ArDrive's Turbo SDK. The key innovation here is making the Turbo SDK work in a browser environment through specific Next.js configurations.
- User connects wallet - ArConnect browser extension is used for wallet connection
- User selects file(s) - Via drag & drop or file picker
- Upload is initiated - Client-side code handles the entire upload process
- ArConnect signer is created - Using the connected wallet
- Turbo SDK client is initialized - With the ArConnect signer
- File is uploaded - Using Turbo SDK's uploadFile method
- URL is generated - For permanent access to the uploaded file
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │ │ ArConnect │ │ Turbo SDK │ │ Arweave │
│ Client │ │ Wallet │ │ │ │ Network │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
│ Connect Wallet │ │ │
│───────────────────> │ │
│ │ Authorize │ │
│ │<──────────────────│ │
│ Grant Permission │ │ │
│<─────────────────── │ │
│ │ │ │
│ Select File │ │ │
│────┐ │ │ │
│ │ │ │ │
│<───┘ │ │ │
│ │ │ │
│ Create ArConnect Signer │ │
│────────────────────────────────────────> │
│ │ │ Initialize Turbo │
│ │ │────┐ │
│ │ │ │ │
│ │ │<───┘ │
│ │ │ │
│ │ │ Upload File │
│ │ │──────────────────>│
│ │ │ │
│ │ │ Transaction ID │
│ │ │<──────────────────│
│ Upload Complete │ │ │
│<─────────────────────────────────────────────────────────│
│ │ │ │
Getting the Turbo SDK to work in a client-side Next.js app required specific configurations:
The next.config.ts file includes webpack overrides to handle Node.js dependencies that Turbo SDK depends on:
// next.config.ts
import webpack from 'webpack';
const nextConfig = {
// Ensure static export for compatibility
output: 'export',
// Disable React strict mode to avoid issues with certain dependencies
reactStrictMode: false,
// Configure webpack for Turbo SDK compatibility
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
// Replace problematic deps with browser versions
'bitcoinjs-lib': 'bitcoinjs-lib/dist/bitcoin.js',
'ecpair': 'ecpair/dist/ecpair.js',
};
// Add polyfills for Node.js built-ins
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
path: false,
os: false,
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
buffer: require.resolve('buffer/'),
};
// Add plugins for browser polyfills
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser',
})
);
return config;
}
};
export default nextConfig;To make the Turbo SDK work client-side, the project uses:
"dependencies": {
"@ar.io/sdk": "^3.9.1",
"@ardrive/turbo-sdk": "^1.23.4-alpha.1",
"@arweave-wallet-kit/browser-wallet-strategy": "^0.1.1",
"@arweave-wallet-kit/core": "^0.1.1",
"@arweave-wallet-kit/react": "^0.3.0",
"arweave-wallet-connector": "^1.0.2",
"bitcoinjs-lib": "^6.1.7",
"react-hot-toast": "^2.5.2",
"util": "^0.12.5"
},
"devDependencies": {
"arweave": "^1.15.7",
"browserify-fs": "^1.0.0",
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.1",
"mime-types": "^3.0.1",
"node-polyfill-webpack-plugin": "^4.1.0",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"stream-browserify": "^3.0.0"
}app/page.tsx- Main entry point with routing setupapp/components/DashboardPage.tsx- Main upload interfaceapp/utils/turboClient.ts- Utility functions for Arweave uploads with Turbo SDKapp/context/AuthContext.tsx- Handles wallet connection stateapp/utils/arweaveWallet.ts- Wallet utilitiesnext.config.ts- Webpack configuration for Turbo SDK browser compatibility
| File | Purpose |
|---|---|
| next.config.ts | Configures webpack to support Turbo SDK in the browser |
| DashboardPage.tsx | Provides the UI for file uploading; handles drag & drop, file selection, and displays upload progress |
| turboClient.ts | Contains core upload functions using Turbo SDK: uploadToArweave() and uploadFolderToArweave() |
| AuthContext.tsx | Manages wallet connection state using ArConnect |
The client-side upload process is implemented as follows:
// app/utils/turboClient.ts
import { TurboFactory, ArconnectSigner } from "@ardrive/turbo-sdk";
// Request wallet permissions
await window.arweaveWallet.connect(['ACCESS_PUBLIC_KEY', 'SIGNATURE', 'SIGN_TRANSACTION']);
// Get wallet address for logging
const walletAddress = await window.arweaveWallet.getActiveAddress();
// Initialize Turbo SDK with ArConnect signer
const signer = new ArconnectSigner(window.arweaveWallet);
const turbo = TurboFactory.authenticated({ signer });// app/utils/turboClient.ts
export async function uploadToArweave(
file: File,
onProgress?: (percent: number) => void
): Promise<{ id: string; url: string }> {
// Check if ArConnect is installed
if (!window.arweaveWallet) {
throw new Error('ArConnect wallet not found. Please install the ArConnect browser extension.');
}
try {
// Request wallet permissions
if (onProgress) onProgress(5);
await window.arweaveWallet.connect(['ACCESS_PUBLIC_KEY', 'SIGNATURE', 'SIGN_TRANSACTION']);
if (onProgress) onProgress(10);
// Get wallet address for logging
const walletAddress = await window.arweaveWallet.getActiveAddress();
console.log('Wallet address:', walletAddress);
// Initialize Turbo SDK with ArConnect signer
const signer = new ArconnectSigner(window.arweaveWallet);
const turbo = TurboFactory.authenticated({ signer });
// Prepare data
if (onProgress) onProgress(20);
console.log('Reading file data...');
const data = await file.arrayBuffer();
// Create transaction
if (onProgress) onProgress(30);
console.log('Creating transaction...');
// Submit transaction via Turbo SDK
console.log('Posting transaction to Arweave network via Turbo...');
const response = await turbo.uploadFile({
fileStreamFactory: () => Buffer.from(data),
fileSizeFactory: () => data.byteLength,
dataItemOpts: {
tags: [
{ name: 'Content-Type', value: getContentType(file) },
{ name: 'App-Name', value: 'Arweave-Upload-Demo' },
{ name: 'App-Version', value: '1.0.0' },
{ name: 'Unix-Time', value: String(Date.now()) },
{ name: 'Filename', value: file.name }
]
}
});
if (onProgress) onProgress(100);
console.log('Transaction posted successfully:', response.id);
return {
id: response.id,
url: `https://arweave.net/${response.id}`
};
} catch (error: unknown) {
console.error('Upload error:', error);
throw new Error(`Upload error: ${error instanceof Error ? error.message : String(error)}`);
}
}- Credit-based uploads - Users don't need to own AR tokens directly
- Lower cost - Turbo provides cost-effective bundling of transactions
- Better privacy - Files go directly from user to Arweave via Turbo
- Simplified architecture - No need for API routes or server-side code
- Better user experience - Real-time progress updates and feedback
- Static exports - Compatible with Next.js static exports, making deployment simpler
- Wallet extension required - Users must have ArConnect installed
- Browser compatibility - Special webpack configuration required
- Alpha build - Using latest alpha build (1.23.4-alpha.1) with improved browser support
- React 19 compatible - Works with the latest Next.js 15.x and React 19
- Static export - Works with Next.js static export mode (
output: 'export')
The Dashboard page provides a complete UI for uploading files including:
- File drag & drop interface
- File selection via file picker
- Upload progress tracking with percentage
- Terminal-like interface for command feedback
- Deployment URL display after successful uploads
The turboClient.ts utility provides several key functions:
getContentType()- Determines the correct MIME type for filesformatBytes()- Formats file sizes in human-readable formuploadToArweave()- Handles file upload with progress trackinguploadFolderToArweave()- Handles multiple file uploads
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.
This project includes scripts to deploy your Next.js app to the Arweave permaweb, providing permanent and decentralized hosting.
- An Arweave wallet file (JWK format)
- Arweave tokens for transaction fees
- Next.js app configured for static export
-
Copy your Arweave wallet file to the project root as
wallet.json:cp your-wallet.json wallet.json
-
Run the deployment script:
pnpm deploy
-
Once completed, your app will be permanently available on Arweave at the URL provided in the console output.
The deployment process:
- Builds your Next.js app as a static site
- Uploads all files to Arweave using the Turbo service
- Creates an Arweave manifest to preserve the directory structure
- Returns a transaction ID that serves as the root of your deployed app
Your app will be available at https://arweave.net/[TRANSACTION_ID]