# Devvit Documentation Devvit is Reddit's Developer Platform that enables developers to build interactive apps, games, and moderation tools that run directly within Reddit subreddits. The platform provides a comprehensive toolkit including a CLI for scaffolding and deployment, a React-like UI framework called Blocks, built-in Redis storage, real-time capabilities, and access to Reddit's API. Apps built on Devvit can be distributed to Reddit's massive user base across iOS, Android, and web platforms, with monetization options through in-app purchases and the Reddit Developer Funds program. This repository contains the official documentation for Devvit, built with Docusaurus. It covers everything from quickstart guides for building games and mod tools to detailed API references for the Reddit API Client, Blocks UI components, event triggers, schedulers, and data storage. The documentation supports version 0.12 (current stable) and includes comprehensive examples, best practices for mobile-first development, and case studies from successful apps like Pixelary and Riddonkulous. ## Devvit CLI - Project Initialization The Devvit CLI provides commands for creating, developing, and deploying apps to Reddit's platform. ```bash # Install the Devvit CLI globally npm install -g devvit # Create a new Devvit app devvit new my-reddit-app cd my-reddit-app # Start local development server devvit start # Upload app to Reddit for testing devvit upload # Deploy app to production devvit publish ``` ## Custom Post Creation Create interactive custom posts that appear natively in Reddit feeds using Devvit's Blocks UI framework. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redditAPI: true, redis: true, }); Devvit.addCustomPostType({ name: 'Interactive Counter', height: 'regular', render: (context) => { const [counter, setCounter] = context.useState(0); return ( Count: {counter} ); }, }); export default Devvit; ``` ## Redis Data Storage Devvit provides built-in Redis storage for persisting app data without external infrastructure. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redis: true }); // Store and retrieve simple key-value data async function saveUserScore(context: Devvit.Context, userId: string, score: number) { const redis = context.redis; // Store a simple value await redis.set(`user:${userId}:score`, score.toString()); // Store with expiration (TTL in seconds) await redis.set(`session:${userId}`, 'active', { expiration: 3600 }); // Retrieve value const savedScore = await redis.get(`user:${userId}:score`); console.log(`User score: ${savedScore}`); // Use sorted sets for leaderboards await redis.zAdd('leaderboard', { member: userId, score: score }); // Get top 10 players const topPlayers = await redis.zRange('leaderboard', 0, 9, { reverse: true }); return topPlayers; } // Hash operations for complex objects async function saveUserProfile(context: Devvit.Context, userId: string) { await context.redis.hSet(`profile:${userId}`, { username: 'player1', level: '5', lastActive: Date.now().toString(), }); const profile = await context.redis.hGetAll(`profile:${userId}`); return profile; } ``` ## Reddit API Client Access Reddit's data and functionality through the built-in Reddit API client. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redditAPI: true }); async function interactWithReddit(context: Devvit.Context) { const reddit = context.reddit; // Get current user information const currentUser = await reddit.getCurrentUser(); console.log(`Current user: ${currentUser?.username}`); // Get subreddit information const subreddit = await reddit.getSubredditById(context.subredditId); console.log(`Subreddit: ${subreddit.name}, Subscribers: ${subreddit.subscriberCount}`); // Create a new post const post = await reddit.submitPost({ subredditName: subreddit.name, title: 'New Game Post', preview: ( Loading game... ), }); // Get comments on a post const comments = await reddit.getComments({ postId: context.postId, limit: 100, sort: 'new', }); // Submit a comment await reddit.submitComment({ id: context.postId, text: 'Great job on level 5!', }); return { user: currentUser, post, comments }; } ``` ## Event Triggers Respond to Reddit events like new posts, comments, and mod actions automatically. ```typescript import { Devvit, TriggerContext } from '@devvit/public-api'; Devvit.configure({ redditAPI: true }); // Trigger on new post creation Devvit.addTrigger({ event: 'PostCreate', onEvent: async (event, context: TriggerContext) => { const post = event.post; console.log(`New post created: ${post?.title}`); // Auto-flair new posts if (post?.title.toLowerCase().includes('[question]')) { await context.reddit.setPostFlair({ postId: post.id, subredditName: event.subreddit?.name ?? '', flairTemplateId: 'question-flair-id', }); } }, }); // Trigger on new comment Devvit.addTrigger({ event: 'CommentCreate', onEvent: async (event, context: TriggerContext) => { const comment = event.comment; const author = event.author; // Track user activity in Redis if (author) { await context.redis.incr(`user:${author.id}:comment_count`); } }, }); // Trigger on app installation Devvit.addTrigger({ event: 'AppInstall', onEvent: async (event, context: TriggerContext) => { console.log(`App installed in: ${event.subreddit?.name}`); // Initialize default settings await context.redis.hSet('app:settings', { welcomeMessage: 'Welcome to the game!', maxPlayersPerGame: '10', }); }, }); export default Devvit; ``` ## Scheduler for Background Jobs Schedule recurring tasks and delayed operations using Devvit's built-in scheduler. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redis: true, redditAPI: true }); // Define a scheduled job handler Devvit.addSchedulerJob({ name: 'daily_leaderboard_reset', onRun: async (event, context) => { // Reset daily scores await context.redis.del('daily_leaderboard'); // Archive yesterday's winners const winners = await context.redis.zRange('leaderboard', 0, 2, { reverse: true }); await context.redis.lPush('past_winners', JSON.stringify({ date: new Date().toISOString(), winners, })); console.log('Daily leaderboard reset completed'); }, }); // Schedule the job (e.g., from a menu action or trigger) Devvit.addMenuItem({ label: 'Start Daily Resets', location: 'subreddit', onPress: async (event, context) => { // Schedule daily at midnight UTC await context.scheduler.runJob({ name: 'daily_leaderboard_reset', cron: '0 0 * * *', // Every day at midnight }); // Or schedule a one-time delayed job await context.scheduler.runJob({ name: 'daily_leaderboard_reset', runAt: new Date(Date.now() + 60000), // Run in 1 minute }); context.ui.showToast('Scheduled daily leaderboard resets!'); }, }); export default Devvit; ``` ## Menu Actions Add custom actions to Reddit's context menus for posts, comments, and subreddits. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redditAPI: true }); // Add action to post context menu Devvit.addMenuItem({ label: 'Create Game from Post', location: 'post', onPress: async (event, context) => { const postId = event.targetId; const post = await context.reddit.getPostById(postId); // Store game data await context.redis.hSet(`game:${postId}`, { title: post.title, createdBy: post.authorName ?? 'unknown', status: 'active', }); context.ui.showToast({ text: 'Game created successfully!' }); }, }); // Add action to comment menu Devvit.addMenuItem({ label: 'Award Points', location: 'comment', forUserType: 'moderator', // Only visible to moderators onPress: async (event, context) => { const comment = await context.reddit.getCommentById(event.targetId); const authorId = comment.authorId; if (authorId) { const newScore = await context.redis.incrBy(`user:${authorId}:points`, 10); context.ui.showToast({ text: `Awarded 10 points! Total: ${newScore}` }); } }, }); // Add subreddit-level action Devvit.addMenuItem({ label: 'View Leaderboard', location: 'subreddit', onPress: async (event, context) => { context.ui.navigateTo(`https://developers.reddit.com/r/${event.targetId}/leaderboard`); }, }); export default Devvit; ``` ## Forms for User Input Create interactive forms to collect user input with validation. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redis: true }); // Define a form const createGameForm = Devvit.createForm( { title: 'Create New Game', fields: [ { name: 'gameName', label: 'Game Name', type: 'string', required: true, helpText: 'Enter a unique name for your game', }, { name: 'maxPlayers', label: 'Maximum Players', type: 'number', defaultValue: 10, }, { name: 'difficulty', label: 'Difficulty', type: 'select', options: [ { label: 'Easy', value: 'easy' }, { label: 'Medium', value: 'medium' }, { label: 'Hard', value: 'hard' }, ], }, { name: 'enableChat', label: 'Enable In-Game Chat', type: 'boolean', defaultValue: true, }, ], acceptLabel: 'Create Game', cancelLabel: 'Cancel', }, async (event, context) => { const { gameName, maxPlayers, difficulty, enableChat } = event.values; // Save game configuration const gameId = `game_${Date.now()}`; await context.redis.hSet(`game:${gameId}`, { name: gameName, maxPlayers: maxPlayers.toString(), difficulty: difficulty[0], chatEnabled: enableChat.toString(), createdAt: new Date().toISOString(), }); context.ui.showToast({ text: `Game "${gameName}" created!` }); } ); // Show form from menu action Devvit.addMenuItem({ label: 'Create Game', location: 'subreddit', onPress: async (event, context) => { context.ui.showForm(createGameForm); }, }); export default Devvit; ``` ## HTTP Fetch for External APIs Make HTTP requests to external services from your Devvit app. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ http: true, // Enable HTTP fetch capability }); async function fetchExternalData(context: Devvit.Context) { // Simple GET request const response = await fetch('https://api.example.com/data'); const data = await response.json(); // POST request with headers and body const postResponse = await fetch('https://api.example.com/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key', }, body: JSON.stringify({ userId: context.userId, action: 'game_complete', score: 100, }), }); if (!postResponse.ok) { throw new Error(`HTTP error: ${postResponse.status}`); } return await postResponse.json(); } // Use in a custom post Devvit.addCustomPostType({ name: 'External Data Display', render: (context) => { const [data, setData] = context.useState(null); const [loading, setLoading] = context.useState(true); context.useAsync(async () => { try { const result = await fetchExternalData(context); setData(result); } finally { setLoading(false); } }); if (loading) { return Loading external data...; } return ( External Data: {JSON.stringify(data)} ); }, }); export default Devvit; ``` ## Realtime Updates Build multiplayer experiences with real-time data synchronization across clients. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redis: true, realtime: true, }); Devvit.addCustomPostType({ name: 'Realtime Game', render: (context) => { const channel = context.useChannel({ name: `game_${context.postId}`, onMessage: (message) => { // Handle incoming messages from other players console.log('Received:', message); if (message.type === 'player_move') { // Update game state based on other player's move setGameState(prev => ({ ...prev, players: { ...prev.players, [message.playerId]: message.position, }, })); } }, }); const [gameState, setGameState] = context.useState({ players: {}, currentTurn: null, }); // Subscribe to channel on mount context.useAsync(async () => { await channel.subscribe(); }); const handleMove = async (x: number, y: number) => { // Send move to all connected clients await channel.send({ type: 'player_move', playerId: context.userId, position: { x, y }, timestamp: Date.now(), }); // Also persist to Redis for players who join later await context.redis.hSet(`game:${context.postId}:state`, { [`player:${context.userId}`]: JSON.stringify({ x, y }), }); }; return ( Multiplayer Game Players online: {Object.keys(gameState.players).length} ); }, }); export default Devvit; ``` ## In-App Purchases (Payments API) Monetize your app with Reddit's built-in payments system using Reddit Gold. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.configure({ redis: true, redditAPI: true, }); // Define purchasable products const products = [ { sku: 'extra_lives_5', name: '5 Extra Lives', goldPrice: 50 }, { sku: 'premium_skin', name: 'Premium Skin Pack', goldPrice: 200 }, { sku: 'hint_pack_10', name: '10 Hints', goldPrice: 25 }, ]; // Handle purchase completion Devvit.addTrigger({ event: 'PurchaseComplete', onEvent: async (event, context) => { const { sku, userId } = event; // Grant purchased items to user switch (sku) { case 'extra_lives_5': await context.redis.incrBy(`user:${userId}:lives`, 5); break; case 'premium_skin': await context.redis.sAdd(`user:${userId}:skins`, 'premium'); break; case 'hint_pack_10': await context.redis.incrBy(`user:${userId}:hints`, 10); break; } console.log(`Purchase completed: ${sku} for user ${userId}`); }, }); Devvit.addCustomPostType({ name: 'Game with Store', render: (context) => { const [lives, setLives] = context.useState(3); const handlePurchase = async (sku: string) => { // Initiate purchase flow context.ui.showToast({ text: 'Opening purchase dialog...' }); await context.reddit.purchaseProduct(sku); }; return ( Game Store Current Lives: {lives} {products.map(product => ( ))} ); }, }); export default Devvit; ``` ## App Settings and Secrets Configure app settings that moderators can customize and securely store API keys. ```typescript import { Devvit } from '@devvit/public-api'; // Define app settings (visible to moderators) Devvit.addSettings([ { name: 'welcomeMessage', label: 'Welcome Message', type: 'string', defaultValue: 'Welcome to the game!', helpText: 'Message shown to new players', }, { name: 'maxDailyPlays', label: 'Maximum Daily Plays', type: 'number', defaultValue: 10, }, { name: 'enableLeaderboard', label: 'Enable Leaderboard', type: 'boolean', defaultValue: true, }, { name: 'externalApiKey', label: 'External API Key', type: 'string', isSecret: true, // Stored securely, not visible after saving helpText: 'API key for external service integration', }, ]); Devvit.addCustomPostType({ name: 'Configurable Game', render: (context) => { const [settings, setSettings] = context.useState(null); context.useAsync(async () => { // Retrieve settings const welcomeMessage = await context.settings.get('welcomeMessage'); const maxDailyPlays = await context.settings.get('maxDailyPlays'); const enableLeaderboard = await context.settings.get('enableLeaderboard'); const apiKey = await context.settings.get('externalApiKey'); setSettings({ welcomeMessage, maxDailyPlays, enableLeaderboard, apiKey }); }); if (!settings) { return Loading settings...; } return ( {settings.welcomeMessage} Daily plays remaining: {settings.maxDailyPlays} {settings.enableLeaderboard && ( )} ); }, }); export default Devvit; ``` ## Blocks UI Components Devvit's Blocks framework provides a React-like component system for building cross-platform UIs. ```typescript import { Devvit } from '@devvit/public-api'; Devvit.addCustomPostType({ name: 'UI Component Showcase', render: (context) => { const [selected, setSelected] = context.useState('home'); return ( {/* Vertical stack with gap and padding */} {/* Text with various styles */} Game Title A fun multiplayer experience {/* Horizontal stack for buttons */} {/* Conditional rendering */} {selected === 'home' && ( Welcome back, player! Last played: 2 hours ago )} {/* Image component */} {/* Spacer for flexible spacing */} {/* Icon component */} 1,234 points ); }, }); export default Devvit; ``` ## Summary Devvit enables developers to create a wide range of Reddit-native applications, from interactive games like Pixelary and Riddonkulous that have attracted tens of thousands of subscribers, to powerful moderation tools like Bot Bouncer and Trending Tattler. The platform's key strengths lie in its integrated ecosystem—developers get built-in storage (Redis), real-time capabilities, Reddit API access, and hosting without managing external infrastructure. The mobile-first design philosophy ensures apps work seamlessly across iOS, Android, and web, while the Blocks UI framework provides cross-platform compatibility with a familiar React-like syntax. For monetization, developers can leverage Reddit's in-app purchases via the Payments API or participate in the Reddit Developer Funds program, which has distributed over $163,000 in a single month to successful app creators. The platform is particularly well-suited for community-driven experiences that generate user-generated content, asynchronous multiplayer games, and sophisticated mod tools. Developers are encouraged to join the r/Devvit community and Discord for support, share feedback through Feedback Fridays, and explore the template library and open-source examples like Pixelary to accelerate development.