High-Performance NewsFeed is an application featuring a news feed with infinite scroll, full-text search, and rich media content. The main focus of the project is deep implementation of virtualization algorithms for elements with dynamic height.
Minimization of Layout Shift and creating efficient virtualization for lists with elements whose size depends on:
- Length of text content
- Loaded media files (images/videos)
- Interactive user actions (text expansion)
| Feature | Description |
|---|---|
| Dynamic height calculation | Rendering elements whose size depends on content |
| Geometry caching | Storing calculated heights for each post id |
| Scroll Anchoring | Maintaining scroll position when content inside elements changes |
- Full-text search — global feed search via backend
- Reset & Re-calculate — complete reset of the virtualized list when query is updated
- Highlighting — highlighting found words in cards
- Media content — images with different aspect ratios and video players
- Interaction — "Show more" button (expand) for long texts
flowchart TB
subgraph Frontend ["Frontend - React + Vite"]
UI[UI Components]
VZ[TanStack Virtual]
RQ[TanStack Query]
end
subgraph Backend ["Backend - NestJS"]
API[REST API]
SVC[Posts Service]
ORM[TypeORM]
end
subgraph Database ["PostgreSQL"]
DB[(Posts Table)]
end
UI --> VZ
VZ --> RQ
RQ -->|HTTP| API
API --> SVC
SVC --> ORM
ORM --> DB
| Technology | Purpose |
|---|---|
| NestJS | Node.js framework |
| TypeORM | Data Mapper pattern |
| PostgreSQL | Storing 10,000+ posts |
| TypeScript | Typing |
| Technology | Purpose |
|---|---|
| React.js | UI library |
| TypeScript | Typing |
| Vite | Build tool |
| TanStack Query | Server State Management |
| TanStack Virtual | Virtualization Engine |
NewsFeed/
├── backend/
├── frontend/
└── doc/
GET /posts?limit=20&cursor=ID&search=text
interface PostsResponse {
items: Post[];
nextCursor: string | null;
hasMore: boolean;
}interface Post {
id: string; // UUID - key for React list keys
title: string;
content: string; // Dynamic text of varying length
attachments: Attachment[];
createdAt: Date;
cursorId: number; // BigInt for Seek Pagination
}
interface Attachment {
type: 'image' | 'video';
url: string;
aspectRatio: number; // Critically important for preventing Layout Shift
}Seek Method (Cursor-based) pagination is used:
- Stable selection when adding new records
- Optimized performance with large data volumes
WHERE cursorId < :cursor
AND (title ILIKE :search OR content ILIKE :search)
ORDER BY cursorId DESC
LIMIT :limitconst rowVirtualizer = useVirtualizer({
count: posts.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 400,
measureElement: (el) => el.getBoundingClientRect().height,
});// Reserving space for media before loading
<div style={{ aspectRatio: `${media.aspectRatio}`, width: '100%' }}>
<img src={media.url} loading="lazy" />
</div>- Node.js 18+
- PostgreSQL 15+
- npm or yarn
# Cloning the repository
git clone <repository-url>
cd NewsFeed
# Installing backend dependencies
cd backend
npm install
# Installing frontend dependencies
cd ../frontend
npm install# Running PostgreSQL via Docker
cd backend
docker-compose up -d
# Running backend
npm run start:dev
# Running frontend (in a new terminal)
cd ../frontend
npm run dev- PRD (Product Requirements Document) — product requirements
- ADR (Architecture Design Record) — architectural decisions
MIT License