ModelBoard is a modern, full-featured platform for hosting, showcasing, and testing AI models β similar to Hugging Face. Built with Next.js 15, TypeScript, Supabase, and real ML inference via HuggingFace API.
- π Landing Page: Beautiful, responsive home page with feature highlights and call-to-action
- π Model Discovery: Browse and search AI models with advanced tag filtering
- π Model Details: Comprehensive model pages with metadata, tags, and statistics
- π Google SSO: Secure authentication via Google OAuth with session persistence
- π€ User Dashboard: Full CRUD functionality for managing your AI models
- π± Fully Responsive: Optimized experience across desktop, tablet, and mobile
- π Dark Mode: Built-in dark mode support throughout the app
- π― Interactive Model Demos: Test models directly in the browser with real-time inference
- π€ HuggingFace Integration: Real ML model inference (not mocked!)
- Text-to-Text (Summarization)
- Image-to-Text (Image Captioning)
- Text-to-Image (Stable Diffusion)
- Sentiment Analysis
- Question Answering
- οΏ½ Public Portfolio Pages: Share your work at
modelboard.app/username - π Public/Private Models: Control visibility of your models
- π€ File Upload Support: Upload preview images and model files directly to Supabase Storage
- π External URLs: Alternative option to use external image/file URLs
- π Notebook Integration: Link to Google Colab or Jupyter notebooks
- π¨ Custom API Endpoints: Override default models with your own
-
Push your code to GitHub
-
Import the project in Vercel
-
Add environment variables:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYHUGGINGFACE_API_TOKEN
-
Deploy!
Ensure your next.config.ts includes image domains:
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '*.supabase.co',
},
{
protocol: 'https',
hostname: 'lh3.googleusercontent.com',
},
{
protocol: 'https',
hostname: 'huggingface.co',
},
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
],
},
};After deploying to Vercel:
- Update Google OAuth redirect URI with your Vercel domain
- Verify Supabase storage buckets are created
- Test file upload functionality
- Confirm RLS policies are active
- Sign in with Google
- Navigate to "My Account"
- Fill in model details:
- Title, description, tags
- Upload preview image (max 5MB) OR paste external URL
- Upload model file (max 500MB) OR paste external URL
- Add notebook URL (Google Colab/Jupyter)
- Select demo type (text-to-text, image-to-text, etc.)
- Optionally add custom API endpoint
- Toggle public/private visibility
- Click "Add Model"
- Navigate to any model detail page
- Scroll to the "Demo" section
- Interact based on demo type:
- Text-to-Text: Enter text to summarize
- Image-to-Text: Upload an image for captioning
- Text-to-Image: Enter a prompt to generate an image
- Sentiment Analysis: Enter text to analyze sentiment
- Question Answering: Provide context and ask a question
- View real-time results
Your public models are automatically available at:
https://modelboard.app/your-username
Share this link to showcase your AI work!
The models table includes the following fields:
| Column | Type | Description |
|---|---|---|
id |
UUID | Primary key |
user_id |
UUID | Foreign key to auth.users |
title |
TEXT | Model name |
description |
TEXT | Model description |
tags |
TEXT[] | Array of tags |
preview_image |
TEXT | External image URL |
preview_image_path |
TEXT | Supabase Storage path |
model_file_path |
TEXT | Supabase Storage path for model files |
notebook_url |
TEXT | Link to Google Colab/Jupyter |
demo_type |
TEXT | One of: text-to-text, image-to-text, text-to-image, sentiment-analysis, question-answering |
api_endpoint |
TEXT | Custom HuggingFace model endpoint |
is_public |
BOOLEAN | Public/private visibility |
created_at |
TIMESTAMP | Creation timestamp |
updated_at |
TIMESTAMP | Last update timestamp |
Two storage buckets are required:
- model-previews: For preview images (max 5MB, public access)
- model-files: For model files (max 500MB, authenticated access)
Files are organized in user-specific folders: {userId}/{filename}
- Node.js 18+ and npm/yarn
- A Supabase account (sign up here)
- Google OAuth credentials (from Google Cloud Console)
- HuggingFace API token (free at huggingface.co)
-
Clone the repository:
git clone https://github.com/Shree-212/ModelBoard.git cd ModelBoard -
Install dependencies:
npm install # or yarn install -
Set up environment variables:
Create a
.env.localfile:NEXT_PUBLIC_SUPABASE_URL=your-project-url NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key HUGGINGFACE_API_TOKEN=your-hf-token
-
Run database migrations:
In your Supabase SQL Editor, run these migrations in order:
a. Create profiles table:
-- From migrations/create_profiles_table.sql (if not exists) CREATE TABLE IF NOT EXISTS public.profiles ( id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, username TEXT UNIQUE, full_name TEXT, avatar_url TEXT, bio TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT now() ); -- Auto-create profile on user signup CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS TRIGGER AS $$ BEGIN INSERT INTO public.profiles (id, username, full_name, avatar_url) VALUES ( NEW.id, COALESCE(NEW.raw_user_meta_data->>'preferred_username', NEW.email), COALESCE(NEW.raw_user_meta_data->>'full_name', NEW.raw_user_meta_data->>'name'), NEW.raw_user_meta_data->>'avatar_url' ); RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
b. Add storage path columns:
-- From migrations/add_storage_paths.sql ALTER TABLE public.models ADD COLUMN IF NOT EXISTS preview_image_path TEXT, ADD COLUMN IF NOT EXISTS model_file_path TEXT, ADD COLUMN IF NOT EXISTS notebook_url TEXT, ADD COLUMN IF NOT EXISTS demo_type TEXT, ADD COLUMN IF NOT EXISTS api_endpoint TEXT, ADD COLUMN IF NOT EXISTS is_public BOOLEAN DEFAULT true;
c. Create storage buckets:
-- From migrations/create_storage_buckets.sql INSERT INTO storage.buckets (id, name, public) VALUES ('model-previews', 'model-previews', true), ('model-files', 'model-files', false) ON CONFLICT (id) DO NOTHING; -- RLS policies for model-previews DROP POLICY IF EXISTS "Users can upload preview images" ON storage.objects; CREATE POLICY "Users can upload preview images" ON storage.objects FOR INSERT WITH CHECK (bucket_id = 'model-previews' AND auth.uid()::text = (storage.foldername(name))[1]); DROP POLICY IF EXISTS "Users can update their preview images" ON storage.objects; CREATE POLICY "Users can update their preview images" ON storage.objects FOR UPDATE USING (bucket_id = 'model-previews' AND auth.uid()::text = (storage.foldername(name))[1]); DROP POLICY IF EXISTS "Users can delete their preview images" ON storage.objects; CREATE POLICY "Users can delete their preview images" ON storage.objects FOR DELETE USING (bucket_id = 'model-previews' AND auth.uid()::text = (storage.foldername(name))[1]); DROP POLICY IF EXISTS "Public preview images are publicly accessible" ON storage.objects; CREATE POLICY "Public preview images are publicly accessible" ON storage.objects FOR SELECT USING (bucket_id = 'model-previews'); -- RLS policies for model-files DROP POLICY IF EXISTS "Users can upload model files" ON storage.objects; CREATE POLICY "Users can upload model files" ON storage.objects FOR INSERT WITH CHECK (bucket_id = 'model-files' AND auth.uid()::text = (storage.foldername(name))[1]); DROP POLICY IF EXISTS "Users can update their model files" ON storage.objects; CREATE POLICY "Users can update their model files" ON storage.objects FOR UPDATE USING (bucket_id = 'model-files' AND auth.uid()::text = (storage.foldername(name))[1]); DROP POLICY IF EXISTS "Users can delete their model files" ON storage.objects; CREATE POLICY "Users can delete their model files" ON storage.objects FOR DELETE USING (bucket_id = 'model-files' AND auth.uid()::text = (storage.foldername(name))[1]); DROP POLICY IF EXISTS "Authenticated users can download model files" ON storage.objects; CREATE POLICY "Authenticated users can download model files" ON storage.objects FOR SELECT USING (bucket_id = 'model-files' AND auth.role() = 'authenticated');
-
Configure Google OAuth:
- Go to Google Cloud Console
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
https://your-project.supabase.co/auth/v1/callback - Add credentials to Supabase Dashboard β Authentication β Providers β Google
-
Start the development server:
npm run dev # or yarn devOpen http://localhost:3000 in your browser.
ModelBoard/
βββ app/ # Next.js App Router
β βββ page.tsx # Landing page
β βββ layout.tsx # Root layout with AuthProvider
β βββ globals.css # Global styles
β βββ models/ # Model routes
β β βββ page.tsx # Model discovery page
β β βββ [id]/ # Dynamic model detail pages
β β βββ page.tsx
β βββ my-account/ # User dashboard
β β βββ page.tsx # CRUD interface with file uploads
β βββ [username]/ # Dynamic portfolio pages
β β βββ page.tsx # Public user profiles
β βββ auth/
β β βββ callback/ # OAuth callback handler
β β βββ route.ts
β βββ api/
β βββ inference/ # HuggingFace API proxy
β βββ route.ts
βββ components/ # Reusable components
β βββ Navbar.tsx # Navigation with auth state
β βββ ModelDemo.tsx # Interactive demo widget
βββ contexts/
β βββ AuthContext.tsx # Global auth state
βββ lib/
β βββ supabase.ts # Supabase client setup
β βββ storage.ts # File upload utilities
β βββ utils.ts # Helper functions
βββ migrations/ # SQL migration scripts
β βββ add_storage_paths.sql # Add new columns
β βββ create_storage_buckets.sql # Setup storage
βββ next.config.ts # Next.js config with image domains
Interactive demo widget that dynamically renders UI based on demo_type:
- text-to-text: Text input β Summarization output
- image-to-text: Image upload β Caption output
- text-to-image: Text prompt β Generated image
- sentiment-analysis: Text input β Sentiment score/label
- question-answering: Context + Question β Answer
Features:
- Real-time inference via HuggingFace API
- Loading states and error handling
- Visual output rendering (images, formatted text)
- Responsive design
File upload helper functions:
uploadFile(): Upload files to Supabase StoragedeleteFile(): Remove files from storageupdateFile(): Replace existing filesvalidateImageFile(): Check image type/size (max 5MB)validateModelFile(): Check model file size (max 500MB)
Dynamic routes for public user profiles:
- Display user info (avatar, bio, username)
- Grid of public models only (
is_public = true) - Shareable URLs:
modelboard.app/username - SEO-friendly with metadata
CREATE TABLE models (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id),
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
tags TEXT[] DEFAULT ARRAY[]::TEXT[],
preview_image TEXT, -- External URL
preview_image_path TEXT, -- Storage path
model_file_path TEXT, -- Storage path for model files
notebook_url TEXT, -- Colab/Jupyter link
demo_type TEXT, -- Demo type identifier
api_endpoint TEXT, -- Custom HuggingFace endpoint
is_public BOOLEAN DEFAULT true, -- Visibility control
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
likes_count INTEGER DEFAULT 0,
downloads_count INTEGER DEFAULT 0
);For complete database setup, see SUPABASE_SETUP.md.
ModelBoard uses Supabase Authentication with Google OAuth via @supabase/ssr:
- Users click "Sign In with Google"
- Google OAuth flow redirects to
/auth/callback - Session is established and persisted using cookies
- Authenticated users gain access to "My Account" and private features
- Session persistence across page refreshes
- Server-side and client-side auth utilities
- Automatic profile creation on user signup
- Global scope logout (signs out from all devices)
- Create OAuth credentials in Google Cloud Console
- Configure Supabase with your Client ID and Secret
- Add authorized redirect URIs
- Add
prompt: 'consent'in OAuth options for proper logout
Detailed instructions: SUPABASE_SETUP.md
ModelBoard uses Tailwind CSS for styling. Customize the theme in tailwind.config.ts:
theme: {
extend: {
colors: {
// Add your custom colors
},
},
}To add new features:
- Create new pages in the
app/directory - Add components in
components/ - Update Supabase schema if needed
- Update RLS policies for security
- Add new demo types in
ModelDemo.tsxif needed
- Push your code to GitHub
- Import the project to Vercel
- Add environment variables in Vercel dashboard
- Update Google OAuth redirect URIs with your production URL
- Update Supabase Site URL and Redirect URLs
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
HUGGINGFACE_API_TOKEN=your_hf_token_hereContributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the ISC License.
- Inspired by Hugging Face
- Built with Supabase
- UI components styled with Tailwind CSS
- ML inference powered by HuggingFace Inference API
For issues and questions:
- Open an issue on GitHub
- Review the Supabase Setup Guide
- Check the Quick Start Guide
Built with β€οΈ by Shree