# 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.