embed-dlsurf-blogs
v1.0.5
Published
Embed DL Surf blog content into any React app — a full blog listing page **and** individual post viewer, all in two components.
Maintainers
Readme
embed-dlsurf-blogs
Embed DL Surf blog content into any React app — a full blog listing page and individual post viewer, all in two components.
Components
| Component | Purpose |
| -------------- | --------------------------------------------------------------- |
| UserHomePage | Shows a creator's featured hero + recent articles grid. |
| BlogRenderer | Renders a single blog post (Tiptap JSON → HTML). |
They are designed to work together: UserHomePage links each article card to a post page (default /blog/{linkSlug}), and you host BlogRenderer on that route to display the full post. The link path is fully customisable via basePath or getPostUrl.
Pre-requirements
- Node.js
>= 18 - React
>= 18 @tiptap/core(peer dependency)
Install
npm install embed-dlsurf-blogs @tiptap/coreor
yarn add embed-dlsurf-blogs @tiptap/coreor
pnpm add embed-dlsurf-blogs @tiptap/coreQuick Start
Theme color (themeColor prop)
Both components accept a themeColor prop to control accent/highlight UI color.
- Default:
#2d66f5 - Accepts any valid CSS color value (hex, rgb, hsl, named color, etc.)
import { UserHomePage, BlogRenderer } from "embed-dlsurf-blogs";
// Listing page accents (dots, hovers, CTA, themed shadows)
<UserHomePage username="rishav" themeColor="#0ea5e9" />;
// Single post accents (top progress bar, badge, themed shadows)
<BlogRenderer user="rishav" linkSlug="my-post" themeColor="#0ea5e9" />;If you omit themeColor, both components use #2d66f5.
1. Add the blog listing page — UserHomePage
Drop <UserHomePage> on the page where you want the blog listing to appear. Pass the DL Surf username of the creator whose posts you want to display.
import { UserHomePage } from "embed-dlsurf-blogs";
export default function BlogIndex() {
return <UserHomePage username="rishav" />;
}That's it — the component fetches the creator's featured + latest posts and renders them in a responsive card grid.
Props
| Prop | Type | Default | Description |
| ------------ | -------------------------- | -------------- | --------------------------------------------------- |
| username | string | (required) | DL Surf username (with or without a leading @). |
| limit | number | 6 | Maximum number of recent articles to show. |
| basePath | string | "/blog" | Base path prepended to every post slug. |
| getPostUrl | (slug: string) => string | — | Custom URL builder. Takes priority over basePath. |
| themeColor | string | "#2d66f5" | Accent color used for interactive/highlight states. |
What happens when a user clicks an article?
Every card links to {basePath}/{linkSlug} (default: /blog/why-django-is-a-game-changer). You need to set up a page at that route in your app and render <BlogRenderer> there — see below.
Customising the post URL
By default all links point to /blog/{slug}. If your app uses a different route structure, pass basePath or getPostUrl:
// Simple — change the base path
<UserHomePage username="rishav" basePath="/articles" />
// → links become /articles/my-post-slug
// Full control — build any URL you want
<UserHomePage
username="rishav"
getPostUrl={(slug) => `/rishav/posts/${slug}`}
/>
// → links become /rishav/posts/my-post-slug
// Change the accent/theme color (default is #2d66f5)
<UserHomePage username="rishav" themeColor="#0ea5e9" />Just make sure the page at that URL renders <BlogRenderer> with the matching slug.
2. Add the single-post page — BlogRenderer
Create a page that matches the /blog/{linkSlug} route and render <BlogRenderer>, passing the username and the slug from the URL.
Next.js (App Router) example
// app/blog/[slug]/page.tsx
import { BlogRenderer } from "embed-dlsurf-blogs";
export default function BlogPost({ params }: { params: { slug: string } }) {
return <BlogRenderer user="rishav" linkSlug={params.slug} />;
}React Router example
// src/pages/BlogPost.tsx
import { useParams } from "react-router-dom";
import { BlogRenderer } from "embed-dlsurf-blogs";
export default function BlogPost() {
const { slug } = useParams<{ slug: string }>();
return <BlogRenderer user="rishav" linkSlug={slug!} />;
}Tip: You can also pass a full
blogUrlinstead ofuser+linkSlug— as long as the last two path segments are{user}/{linkSlug}.
Props
| Prop | Type | Description |
| ------------ | ---------- | -------------------------------------------------------------------- |
| post | BlogPost | Pre-fetched post object — skips the internal fetch. |
| user | string | DL Surf username. |
| linkSlug | string | URL-friendly slug that identifies the post. |
| blogUrl | string | Full blog URL (user + slug are parsed from the path). |
| themeColor | string | Accent color used for progress/highlight states (#2d66f5 default). |
Provide one of:
post(pre-fetched data), oruser+linkSlug, orblogUrl
Putting it all together
Your app (using default basePath="/blog")
├── /blog → <UserHomePage username="rishav" />
│ ↳ each card links to /blog/{linkSlug}
│
└── /blog/:slug → <BlogRenderer user="rishav" linkSlug={slug} />
↳ renders the full postOr with a custom base path:
Your app (basePath="/articles")
├── /articles → <UserHomePage username="rishav" basePath="/articles" />
│ ↳ each card links to /articles/{linkSlug}
│
└── /articles/:slug → <BlogRenderer user="rishav" linkSlug={slug} />API Response Shape (for reference)
The upstream doc API returns:
{
"status": "success",
"message": "Document retrieved successfully",
"data": {
"id": 1345,
"title": "6 Years of dlsurf, 300000+ Users & Counting",
"content_json": "{\"type\":\"doc\",\"content\":[...]}",
"thumbnail_path": "media/default_thumbnail/thumbnail_b2.png",
"keywords": "[]",
"followers_only": false,
"visibility": "public",
"created_at": "2026-01-01T08:02:57.253021Z",
"link_slug": "happy-new-year-6-years-of-building-dlsurf-300000-users-and-counting",
"updated_at": "2026-01-01T10:16:04.803993Z",
"profile": {
"username": "arjun",
"display_name": "Arjun Ghimire",
"account_level": "6",
"profile_picture": "/media/profile_picture/efd953bb1b.jpeg"
},
"ads_step": 0,
"banner_ads": false,
"video_ads": false
}
}Exported API
| Export | Type |
| -------------- | ---------------------- |
| BlogRenderer | Named + default export |
| UserHomePage | Named export |
Internal Behavior
Both components:
- Fetch post data internally (no manual API calls needed).
- Use scoped CSS class names to avoid style collisions.
- Render Tiptap JSON via an SSR-safe HTML renderer.
BlogRenderer additionally:
- Strips outer
prosewrappers. - Generates a collapsible Table of Contents from
h2/h3headings. - Shows a reading-progress bar and social-share buttons.
UserHomePage additionally:
- Responds to its own container width (CSS container queries) — works in sidebars, panels, or full-width layouts.
- Auto-rotates through featured posts every 4 seconds.
License
ISC
