Package Exports
- react-newsfeed
- react-newsfeed/dist/index.js
- react-newsfeed/dist/index.mjs
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-newsfeed) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
React Newsfeed Component
A customizable social media post component for React applications that mimics popular social media feeds with features like likes, comments, and image galleries.
Authors
Features
📝 Social media post component with author info, content, and tags
🖼️ Image gallery support with multiple images
❤️ Like functionality with count
💬 Comment system with replies
🔗 Share functionality (using Web Share API when available)
📱 Responsive design
🎨 Customizable options menu
Installation
npm install react-newsfeed
# or
yarn add react-newsfeed
Usage
import "./App.css";
import { Post } from "react-newsfeed";
import { useState } from "react";
import { BookmarkCheck, Bug, Share } from "lucide-react";
interface PostData {
id: string;
author: {
name: string;
avatar: string;
timeAgo: string;
};
content: string;
tags: string[];
images: Array<{
id: string;
url: string;
alt: string;
}>;
liked: boolean;
likeCount: number;
comments: Array<{
id: string;
author: string;
avatar: string;
content: string;
timestamp: string;
likes: number;
liked: boolean;
replies: any[];
showReplies: boolean;
showReplyInput: boolean;
}>;
}
function App() {
const [posts, setPosts] = useState<PostData[]>([
{
id: "1",
author: {
name: "Panda Media",
avatar:
"https://images.unsplash.com/profile-1749556385385-1235419e91caimage?w=32&dpr=1&crop=faces&bg=%23fff&h=32&auto=format&fit=crop&q=60&ixlib=rb-4.1.0",
timeAgo: "20h",
},
content: "Check out these amazing pandas!",
tags: ["panda", "nature", "photography"],
images: [
{
id: "1",
url: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
alt: "Image 1",
},
{
id: "2",
url: "https://plus.unsplash.com/premium_photo-1675882505334-382d4cb3d718?q=80&w=1165&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
alt: "Image 2",
},
],
liked: false,
likeCount: 124,
comments: [
{
id: "1",
author: "User 1",
avatar: "https://img.icons8.com/color/48/user-male-circle--v5.png",
content: "Great post!",
timestamp: "2h",
likes: 5,
liked: false,
replies: [],
showReplies: false,
showReplyInput: false,
},
],
},
{
id: "2",
author: {
name: "Wildlife Photography",
avatar:
"https://images.unsplash.com/photo-1564564244660-5d73c057f2d2?q=80&w=1476&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
timeAgo: "5h",
},
content:
"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
tags: ["nature", "photography", "hiking"],
images: [
{
id: "1",
url: "https://images.unsplash.com/photo-1548347480-50e99d864837?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
alt: "Nature shot",
},
],
liked: true,
likeCount: 89,
comments: [],
},
]);
const handleLikePost = (postId: string, liked: boolean) => {
setPosts(
posts.map((post) =>
post.id === postId
? {
...post,
liked,
likeCount: liked ? post.likeCount + 1 : post.likeCount - 1,
}
: post
)
);
};
const handleAddComment = (postId: string, content: string) => {
const newComment = {
id: Date.now().toString(),
author: "Current User",
avatar: "https://img.icons8.com/color/48/user-male-circle--v5.png",
content,
timestamp: "Just now",
likes: 0,
liked: false,
replies: [],
showReplies: false,
showReplyInput: false,
};
setPosts(
posts.map((post) =>
post.id === postId
? {
...post,
comments: [newComment, ...post.comments],
}
: post
)
);
return newComment;
};
const handleLikeComment = (postId: string, commentId: string) => {
setPosts(
posts.map((post) =>
post.id === postId
? {
...post,
comments: post.comments.map((comment) =>
comment.id === commentId
? {
...comment,
liked: !comment.liked,
likes: comment.liked
? comment.likes - 1
: comment.likes + 1,
}
: comment
),
}
: post
)
);
};
const handleAddReply = (
postId: string,
commentId: string,
content: string
) => {
const newReply = {
id: `${commentId}-${Date.now()}`,
author: "Current User",
avatar: "https://img.icons8.com/color/48/user-male-circle--v5.png",
content,
timestamp: "Just now",
likes: 0,
liked: false,
};
setPosts(
posts.map((post) =>
post.id === postId
? {
...post,
comments: post.comments.map((comment) =>
comment.id === commentId
? {
...comment,
replies: [...comment.replies, newReply],
}
: comment
),
}
: post
)
);
return newReply;
};
const handleLikeReply = (
postId: string,
commentId: string,
replyId: string
) => {
console.log(postId, commentId, replyId);
setPosts(
posts.map((post) =>
post.id === postId
? {
...post,
comments: post.comments.map((comment) =>
comment.id === commentId
? {
...comment,
replies: comment.replies.map((reply) =>
reply.id === replyId
? {
...reply,
liked: !reply.liked,
likes: reply.liked
? reply.likes - 1
: reply.likes + 1,
}
: reply
),
}
: comment
),
}
: post
)
);
};
const handleShareClick = (postId: string) => {
const postToShare = posts.find((post) => post.id === postId);
if (postToShare) {
if (navigator.share) {
// Web Share API
navigator
.share({
title: `Post by ${postToShare.author.name}`,
text: postToShare.content,
url: window.location.href,
})
.catch((err) => {
console.error("Error sharing:", err);
});
} else {
// Fallback for browsers without Share API
console.log("Shared post:", postId);
alert(
`Sharing: "${postToShare.content}" by ${postToShare.author.name}`
);
}
}
};
return (
<div className="max-w-lg mx-auto p-4 space-y-4">
{posts.map((post) => (
<Post
key={post.id}
author={post.author}
content={post.content}
tags={post.tags}
options={[
{
title: "Share",
action: () => console.log("Sharing post..."),
icon: <Share />,
},
{
title: "Report",
action: () => alert("Reported!"),
icon: <Bug />,
},
{
title: "Save",
action: () => console.log("Sharing post..."),
icon: <BookmarkCheck />,
},
]}
images={post.images}
initialLiked={post.liked}
initialLikeCount={post.likeCount}
initialComments={post.comments}
onLikePost={(liked) => handleLikePost(post.id, liked)}
onAddComment={(content) => handleAddComment(post.id, content)}
onLikeComment={(commentId) => handleLikeComment(post.id, commentId)}
onAddReply={(commentId, content) =>
handleAddReply(post.id, commentId, content)
}
onLikeReply={(commentId, replyId) =>
handleLikeReply(post.id, commentId, replyId)
}
onClickShare={() => handleShareClick(post.id)}
/>
))}
</div>
);
}
export default App;
Props
Props
Prop | Type | Description |
---|---|---|
author | object |
Author information (contains name , avatar , timeAgo ) |
content | string |
Post content text |
tags | string[] |
Array of tags |
images | Array<{id, url, alt}> |
Array of images to display |
options | Array<{title, action, icon}> |
Array of options for the post menu |
initialLiked | boolean |
Initial liked state |
initialLikeCount | number |
Initial like count |
initialComments | array |
Initial comments array |
onLikePost | function |
Callback when post is liked/unliked |
onAddComment | function |
Callback when comment is added |
onLikeComment | function |
Callback when comment is liked |
onAddReply | function |
Callback when reply is added |
onLikeReply | function |
Callback when reply is liked |
onClickShare | function |
Callback when share button is clicked |
Demo Output
Dependencies
React
Tailwindcss@3
Modern browser with Web Share API support for native sharing
Browser Support
The component works in all modern browsers. The share functionality will use the Web Share API when available, with a fallback to a simple alert when not supported.
License
MIT