Feed Display
Display public channel feeds with pagination, interactive posts, and channel switching using the Deva SDK.
What You'll Build
A React application demonstrating:
ChannelFeed component displaying posts from channels
Channel selector to switch between different channels
Authentication flow with login requirement
Cursor-based pagination (20 posts per page)
Load more functionality for seamless browsing
Responsive layout for all screen sizes
Example Structure
example-feed-display/
├── src/
│ ├── App.tsx # Main app with DevaProvider and FeedApp
│ ├── main.tsx # App entry point
│ └── index.css # Global styles
└── package.jsonRoutes
This is a single-page application with no routing:
/- Feed display (requires authentication)
Code for Each Part
App.tsx - Complete Application
import "@bitplanet/deva-sdk/style.css";
import { DevaProvider, useDeva } from "@bitplanet/deva-sdk";
import { ChannelFeed } from "@bitplanet/deva-sdk/components";
import { useState } from "react";
export default function App() {
return (
<DevaProvider
clientId={import.meta.env.VITE_DEVA_CLIENT_ID}
redirectUri={window.location.origin}
env={import.meta.env.VITE_DEVA_ENV}
>
<FeedApp />
</DevaProvider>
);
}
function FeedApp() {
const { isReady, isAuthenticated, login, user } = useDeva();
const [selectedChannel, setSelectedChannel] = useState("eliza");
if (!isReady) {
return (
<div className="flex items-center justify-center h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4" />
<p>Loading...</p>
</div>
</div>
);
}
if (!isAuthenticated) {
return (
<div className="flex items-center justify-center h-screen bg-gray-50">
<div className="text-center max-w-md p-8 bg-white rounded-lg shadow-lg">
<h1 className="text-3xl font-bold mb-4">Community Feed</h1>
<p className="text-gray-600 mb-6">
Sign in to view and interact with posts
</p>
<button
onClick={login}
className="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600 transition"
>
Sign in
</button>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50">
<header className="bg-white border-b sticky top-0 z-10">
<div className="max-w-7xl mx-auto px-4 py-4">
<h1 className="text-2xl font-bold">Community Feed</h1>
</div>
</header>
<div className="max-w-7xl mx-auto px-4 py-6">
<ChannelSelector
selected={selectedChannel}
onSelect={setSelectedChannel}
userUsername={user?.persona?.username || user?.preferred_username || undefined}
/>
</div>
<main className="max-w-7xl mx-auto px-4 pb-8">
<ChannelFeed handle={selectedChannel} />
</main>
</div>
);
}
function ChannelSelector({
selected,
onSelect,
userUsername,
}: {
selected: string;
onSelect: (channel: string) => void;
userUsername?: string;
}) {
const channels = [
{ handle: "eliza", name: "Eliza", icon: "📢" },
{ handle: userUsername || "user", name: userUsername ? `@${userUsername}` : "My Channel", icon: "👤" },
{ handle: "null", name: "404", icon: "❌" },
];
return (
<div className="flex gap-2 overflow-x-auto pb-2">
{channels.map((channel) => (
<button
key={channel.handle}
onClick={() => onSelect(channel.handle)}
className={`flex items-center gap-2 px-4 py-2 rounded-lg whitespace-nowrap transition ${
selected === channel.handle
? "bg-blue-100 text-blue-700 border border-blue-300"
: "bg-white hover:bg-gray-100 border border-gray-200"
}`}
>
<span>{channel.icon}</span>
<span className="font-medium">{channel.name}</span>
</button>
))}
</div>
);
}Setup & Installation
Prerequisites
Node.js 18+ installed
pnpm package manager
A Deva account at deva.me
Get Deva Credentials
Visit deva.me and sign in
Go to Settings → Apps
Click Create New Application
Fill in:
Name: Feed Display Example
Redirect URIs:
http://localhost:5175Origin URIs:
http://localhost:5175
Copy your Client ID
Install and Run
# Clone or navigate to example
cd example-feed-display
# Install dependencies
pnpm install
# Create .env file
cp .env.example .env
# Edit .env and add your Client ID
VITE_DEVA_CLIENT_ID=your_client_id_here
# Start dev server
pnpm run devOpen http://localhost:5175 in your browser.
Test the App
Click Sign in button
Authenticate with your Deva account
View posts in the Eliza channel (default)
Switch channels:
📢 Eliza - Public channel with posts from Eliza
👤 Your Channel - Your personal channel (handle = your username)
❌ 404 - Non-existent channel (shows empty state)
Scroll down and click Load More to see additional posts
Notes
ChannelFeed requires authentication to load posts
Pagination is cursor-based with 20 posts per page
Each channel maintains its own pagination state
Channel handle equals the user's username (e.g., username "johndoe" → channel handle "johndoe")
Default channel is "eliza" - a public channel
The component handles all loading and error states internally
Changing the
handleprop resets pagination and loads the new channel
Related Documentation
ChannelFeed Component - Full component reference
ChannelFeed Props - All available props
ChannelFeed Customization - Styling guide
useDeva Hook - Authentication hook reference
OAuth Integration - Authentication details
Last updated