Add AI-powered chat to your portfolio in 1 line of code.
Recruiters can ask the AI about:
- β Your background - Experience, education, skills
- β Your projects - What you built, technologies used
- β Your tech stack - Languages, frameworks, tools
- β Job fit - Whether you're eligible for their role
No more scrolling through resumes - recruiters get instant, accurate answers about you!
π‘ Note: While designed for portfolios, you can integrate ChatFolio into any application! Connect it to your data source (documents, database, API) and let users ask questions about your content. Perfect for documentation sites, knowledge bases, customer support, and more.
Static portfolio - recruiters have to read everything manually
Interactive AI chat - recruiters ask questions and get instant answers
The AI has access to your resume and can answer questions about your background, projects, and skills
npm install chatfolioIn your page.tsx (or any React component):
import { AutoChat } from 'chatfolio'
export default function Home() {
return (
<div>
<h1>My Portfolio</h1>
{/* That's it! One line of code */}
<AutoChat />
</div>
)
}Done! Your portfolio now has an AI chat widget. π
Create app/api/chat/route.ts (Next.js) or your API endpoint:
import { createBackendHandler } from 'chatfolio/server'
export const { POST, GET } = createBackendHandler({
// LLM Provider (choose one)
llmProvider: 'anthropic', // or 'openai', 'google', 'groq', 'openrouter'
llmApiKey: process.env.ANTHROPIC_API_KEY!,
llmModel: 'claude-sonnet-4-20250514', // Model name
// Supermemory (Document Search)
supermemoryApiKey: process.env.SUPERMEMORY_API_KEY!,
supermemoryContainer: 'portfolio', // Your container name
// Optional: Customize AI behavior
systemPrompt: `You are a helpful AI assistant for a portfolio website.
Answer questions about the person's background, projects, and skills.
Be professional and accurate.`,
temperature: 0.7,
streaming: true // Enable real-time streaming responses
})Create .env.local:
# Required
SUPERMEMORY_API_KEY=sm_your_key_here
SUPERMEMORY_CONTAINER=portfolio
# Choose your LLM provider
ANTHROPIC_API_KEY=sk-ant-your_key_here
# OR
OPENAI_API_KEY=sk-your_key_here
# OR
GOOGLE_API_KEY=your_google_key_here
# OR
OPENROUTER_API_KEY=sk-or-your_key_hereThe AI needs your resume to answer questions! Upload it via Supermemory Console (recommended for first-time users):
- Go to Supermemory Console
- Sign in with your API key
- Create a container (e.g.,
portfolio) - Upload your resume PDF or text file
- That's it! The AI can now answer questions about your resume
Alternative: Programmatic Upload
If you prefer to upload via code:
// app/api/upload/route.ts
import { uploadToSupermemory } from 'chatfolio/server'
export async function POST(req: Request) {
const formData = await req.formData()
const file = formData.get('file') as File
const result = await uploadToSupermemory(
process.env.SUPERMEMORY_API_KEY!,
{
file,
title: 'My Resume',
container: 'portfolio'
}
)
return Response.json(result)
}The AutoChat component has 40+ customization props:
<AutoChat
// === Layout & Sizing ===
inputHeight="60px"
maxChatHeight="500px"
maxWidth="800px"
// === Outer Container ===
chatContainerBackground="white"
chatContainerShadow="0 4px 24px rgba(0,0,0,0.08)"
borderRadius="16px"
// === Input Field ===
inputFieldBackground="rgba(255, 255, 255, 0.95)"
inputFieldBorderColor="rgba(209, 213, 219, 0.8)"
inputFieldFocusBorderColor="rgba(147, 51, 234, 0.6)"
inputTextColor="#1f2937"
inputPlaceholderColor="rgba(107, 114, 128, 0.6)"
// === Input Area (Bottom Section) ===
inputAreaBackground="rgba(249, 250, 251, 0.8)"
// === Typography ===
fontSize="16px"
fontFamily="Inter, sans-serif"
// === Message Bubbles ===
userMessageBackground="linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)"
userMessageTextColor="#ffffff"
assistantMessageBackground="rgba(243, 244, 246, 0.95)"
assistantMessageTextColor="#1f2937"
messagePadding="12px 16px"
messageGap="12px"
// === Submit Button ===
submitButtonSize="44px"
submitButtonBackground="linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)"
submitButtonIconColor="#ffffff"
// === Scrollbar ===
scrollbarWidth="6px"
scrollbarThumbColor="rgba(156, 163, 175, 0.4)"
// === Behavior ===
placeholder="Ask me anything..."
collapseOnOutsideClick={false}
expandAnimationDuration="0.4s"
/>Dark Mode:
<AutoChat
chatContainerBackground="rgba(17, 24, 39, 0.95)"
inputFieldBackground="rgba(31, 41, 55, 0.8)"
inputTextColor="#f9fafb"
userMessageBackground="linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)"
assistantMessageBackground="rgba(31, 41, 55, 0.8)"
/>Minimal:
<AutoChat
borderRadius="20px"
inputFieldBackground="white"
inputFieldBorderColor="#e5e7eb"
userMessageBackground="#3b82f6"
assistantMessageBackground="#f3f4f6"
/><AutoChat
className="my-custom-chat"
// ... other props
/>Then in your CSS:
/* Move button outside input field */
.my-custom-chat .autochat-input-form {
display: flex;
flex-direction: column;
gap: 12px;
}
.my-custom-chat .autochat-submit {
align-self: flex-end;
margin-top: 8px;
}/* In globals.css or component CSS */
.autochat-input-form {
display: flex !important;
flex-direction: column !important;
}
.autochat-submit {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
}| Class | What It Controls |
|---|---|
.autochat-root |
Entire widget container |
.autochat-container |
Main chat box |
.autochat-messages |
Messages area |
.autochat-message |
Individual message |
.autochat-bubble |
Message bubble |
.autochat-input-wrapper |
Input area wrapper |
.autochat-input-form |
Form (input + button container) |
.autochat-input |
Text input field |
.autochat-submit |
Submit button |
.my-chat .autochat-input-wrapper {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
}
.my-chat .autochat-input-form {
flex: 1;
}
.my-chat .autochat-submit {
flex-shrink: 0;
/* Button is now outside the form */
}| Provider | Models | Setup |
|---|---|---|
| Anthropic | Claude 3.5 Sonnet, Claude 3 Opus | llmProvider: 'anthropic' |
| OpenAI | GPT-4o, GPT-4 Turbo | llmProvider: 'openai' |
| Gemini 1.5 Flash, Gemini Pro | llmProvider: 'google' |
|
| Groq | Llama 3.3 70B, Mixtral | llmProvider: 'groq' |
| OpenRouter | 100+ models | llmProvider: 'openrouter' |
// Use Google Gemini
export const { POST, GET } = createBackendHandler({
llmProvider: 'google',
llmApiKey: process.env.GOOGLE_API_KEY!,
llmModel: 'gemini-1.5-flash',
// ... rest
})
// Use OpenAI
export const { POST, GET } = createBackendHandler({
llmProvider: 'openai',
llmApiKey: process.env.OPENAI_API_KEY!,
llmModel: 'gpt-4o',
// ... rest
})- β
One-line integration - Just import
<AutoChat /> - β 40+ customization props - Full control over appearance
- β CSS override support - Complete styling flexibility
- β Multi-provider support - Anthropic, OpenAI, Google, Groq, OpenRouter
- β Streaming responses - Real-time typing effect
- β Mobile responsive - Works on all devices
- β Session management - Remembers conversation
- β Document search - Powered by Supermemory
- β Zero conversation saving - Only fetches, never saves (privacy-first)
- Recruiters can quickly assess your fit
- Answer questions about your experience
- Showcase your projects interactively
- Add AI chat to any portfolio
- Customize to match your brand
- Full control over UI/UX
- Ask specific questions about candidates
- Get instant answers about skills
- No need to read entire resumes
inputHeight- Input field heightmaxChatHeight- Max height when expandedmaxWidth- Widget width
chatContainerBackground- Main container backgroundinputFieldBackground- Input backgroundinputFieldBorderColor- Input borderinputFieldFocusBorderColor- Focus borderuserMessageBackground- Your messages backgroundassistantMessageBackground- AI messages backgroundsubmitButtonBackground- Button background
fontSize- Text sizefontFamily- Font family
placeholder- Input placeholder textcollapseOnOutsideClick- Auto-collapse on outside clickexpandAnimationDuration- Animation speed
See full list in Props Documentation
- Check your API keys in
.env.local - Make sure you uploaded documents to Supermemory
- Check browser console for errors
- Make sure you imported
AutoChatcorrectly - Check if your backend route is working
- Verify
apiPathprop matches your route
- Use
!importantfor CSS overrides - Check CSS specificity
- Use
classNameprop for scoped styles
MIT
Contributions welcome! Please open an issue or PR.
Having issues? Open a GitHub issue or check the documentation.
See GitHub Issues for the complete TODO list, including:
- OpenRouter conversation memory improvements
- More chat UI templates
- Vanilla JavaScript implementation
- And more...
Made with β€οΈ for developers who want to stand out


