A web app to help you design things, local, offline, on device. In your browser.
TypeScript
53.1%
JSON
45.6%
CSS
0.9%
Markdown
0.3%
JavaScript
0.1%
// Unsplash API Client
// Free tier: 50 requests/hour (demo), unlimited on production approval
// Docs: https://unsplash.com/documentation
export interface UnsplashPhoto {
id: string;
created_at: string;
updated_at: string;
width: number;
height: number;
color: string;
blur_hash: string;
description: string | null;
alt_description: string | null;
urls: {
raw: string;
full: string;
regular: string;
small: string;
thumb: string;
small_s3: string;
};
links: {
self: string;
html: string;
download: string;
download_location: string;
};
user: {
id: string;
username: string;
name: string;
portfolio_url: string | null;
bio: string | null;
profile_image: {
small: string;
medium: string;
large: string;
};
links: {
self: string;
html: string;
photos: string;
};
};
}
export interface UnsplashSearchResponse {
total: number;
total_pages: number;
results: UnsplashPhoto[];
}
const UNSPLASH_ACCESS_KEY = process.env.NEXT_PUBLIC_UNSPLASH_ACCESS_KEY || '';
const UNSPLASH_BASE_URL = 'https://api.unsplash.com';
async function fetchUnsplash<T>(endpoint: string): Promise<T> {
if (!UNSPLASH_ACCESS_KEY) {
throw new Error('Unsplash API key not configured. Add NEXT_PUBLIC_UNSPLASH_ACCESS_KEY to .env.local');
}
const response = await fetch(`${UNSPLASH_BASE_URL}${endpoint}`, {
headers: {
Authorization: `Client-ID ${UNSPLASH_ACCESS_KEY}`,
},
});
if (!response.ok) {
throw new Error(`Unsplash API error: ${response.status} ${response.statusText}`);
}
return response.json();
}
export async function searchUnsplashPhotos(
query: string,
options: {
page?: number;
per_page?: number;
orientation?: 'landscape' | 'portrait' | 'squarish';
color?: string;
order_by?: 'relevant' | 'latest';
} = {}
): Promise<UnsplashSearchResponse> {
const params = new URLSearchParams({
query,
page: String(options.page || 1),
per_page: String(options.per_page || 20),
});
if (options.orientation) params.set('orientation', options.orientation);
if (options.color) params.set('color', options.color);
if (options.order_by) params.set('order_by', options.order_by);
return fetchUnsplash<UnsplashSearchResponse>(`/search/photos?${params}`);
}
export async function getRandomPhotos(
options: {
count?: number;
query?: string;
orientation?: 'landscape' | 'portrait' | 'squarish';
featured?: boolean;
} = {}
): Promise<UnsplashPhoto[]> {
const params = new URLSearchParams({
count: String(options.count || 10),
});
if (options.query) params.set('query', options.query);
if (options.orientation) params.set('orientation', options.orientation);
if (options.featured) params.set('featured', 'true');
return fetchUnsplash<UnsplashPhoto[]>(`/photos/random?${params}`);
}
export async function getPhoto(id: string): Promise<UnsplashPhoto> {
return fetchUnsplash<UnsplashPhoto>(`/photos/${id}`);
}
// Track download for Unsplash attribution requirement
export async function trackDownload(downloadLocation: string): Promise<void> {
if (!UNSPLASH_ACCESS_KEY) return;
try {
await fetch(downloadLocation, {
headers: {
Authorization: `Client-ID ${UNSPLASH_ACCESS_KEY}`,
},
});
} catch {
// Silently fail - tracking is best effort
}
}
// Predefined collections for quick browsing
export const UNSPLASH_COLLECTIONS = [
{ id: 'technology', label: 'Technology', query: 'technology software' },
{ id: 'gradients', label: 'Gradients', query: 'gradient abstract colorful' },
{ id: 'textures', label: 'Textures', query: 'texture pattern' },
{ id: 'dark', label: 'Dark Mode', query: 'dark moody black' },
{ id: 'minimal', label: 'Minimal', query: 'minimal white clean' },
{ id: 'nature', label: 'Nature', query: 'nature landscape scenic' },
{ id: '3d', label: '3D Renders', query: '3d render abstract' },
{ id: 'workspace', label: 'Workspace', query: 'desk workspace office' },
] as const;
About
A web app to help you design things, local, offline, on device. In your browser.
0 stars
0 forks