Skip to main content
Version: 1.0.3

Activity Feed Page

The Activity Feed Page is a high-level template for displaying chronological events such as user actions, system updates, billing events, and security alerts. It provides:

  • A chronological event list, sorted newest-first.
  • Event type icons and badges for quick scanning (auth, user, billing, security, etc.).
  • Actor information (who performed the action).
  • Relative or absolute timestamps (e.g. "2 hours ago" or "Mar 12, 2026, 08:45").
  • Filter by event type chips and search across titles, descriptions, actors, and context.
  • Either pagination or infinite load-more behaviour.

The page is composable: you can use the full ActivityFeedPage component, or mix and match layout, header, filters, list, row, and pagination building blocks to fit your dashboard.

Activity Feed

Track recent actions, system updates, and user activity.

Events

Newest first · Filter by type · Search across titles, actors, and descriptions

Today

User signed in

Event typeAuth
Alex MorganAdmin

Successful login from a trusted device. (event 1)

New user invited

Event typeUser
Sara Kim@sara

An invitation was sent to join the workspace. (event 2)

Security policy updated

Event typeSecurity
Ignix SystemService

MFA enforcement was enabled for all admins. (event 3)

System configuration changed

Event typeSystem
Priya PatelSupport

Updated environment variables for the deployment. (event 4)

Invoice paid

Event typeBilling
Diego RiveraBillingInvoice #1024

Payment succeeded for the monthly subscription. (event 5)

Order processed

Event typeOrder
Alex MorganAdminOrder #8821

Order moved to the next stage in fulfillment. (event 6)

Document updated

Event typeDocument
Ignix SystemServiceOnboarding SOP

Edited a shared SOP for onboarding. (event 8)

Comment added

Event typeComment
Sara Kim@saraRelease notes

Left feedback on the latest deployment notes. (event 7)

Maintenance scheduled

Event typeSchedule
Priya PatelSupportSun 02:00–03:00

Planned maintenance window was created. (event 9)

Unusual activity detected

Event typeWarning
Diego RiveraBillingIP flagged

Multiple failed sign-in attempts were detected. (event 10)

Installation

ignix add component activity-feed-page

Features

  • Chronological event list: events are always sorted newest-first by their occurredAt timestamp.
  • Event type badges: each event shows an icon and badge (auth, user, security, billing, etc.) with theme-aware colors.
  • Actor + context: display who performed the action plus extra context like invoice or order IDs.
  • Relative or absolute time: show friendly "2 hours ago" strings or full absolute timestamps.
  • Search + filters: filter by event type and search across titles, descriptions, actors, and context.
  • Pagination or infinite scroll: choose between numbered pages or a "Load more" / infinite pattern.
  • Composable sections: ActivityFeedLayout, ActivityFeedHeader, ActivityFeedFilters, ActivityFeedList, ActivityEventRow, and ActivityFeedPagination can be used independently.

Props

Page props (ActivityFeedPage)

type ActivityEventType =
| "authentication"
| "user"
| "security"
| "system"
| "billing"
| "order"
| "comment"
| "document"
| "schedule"
| "warning";

interface ActivityActor {
name: string;
meta?: string;
avatarUrl?: string;
}

interface ActivityEvent {
id: string;
type: ActivityEventType;
actor: ActivityActor;
occurredAt: Date;
title: string;
description: string;
contextLabel?: string;
}

type TimestampMode = "relative" | "absolute";

type FeedPagingMode = "pagination" | "infinite";

interface ActivityFeedFilterState {
type: ActivityEventType | null;
query: string;
}
PropTypeDescription
eventsActivityEvent[]Events to display in the feed; they are sorted newest-first by occurredAt.
titlestring (optional)Page title; defaults to "Activity Feed".
descriptionstring (optional)Subtitle shown under the title.
timestampMode"relative" | "absolute" (optional)Controls whether timestamps are rendered as "2 hours ago" or as absolute locale strings. Defaults to "relative".
pagingMode"pagination" | "infinite" (optional)Whether to paginate with numbered pages or use an infinite "Load more" pattern. Defaults to "pagination".
pageSizenumber (optional)Number of events per page or per load. Defaults to 10.
filterStateActivityFeedFilterState (optional)Controlled filter state (type + search query). When omitted, the page manages its own filter state.
onFilterChange(next: ActivityFeedFilterState) => void (optional)Called when the user changes the type filter or search query. Use this to control filterState from above.
classNamestring (optional)Additional class names for the outer layout wrapper.

Composable component props

ActivityFeedLayout

interface ActivityFeedLayoutProps {
children: React.ReactNode;
className?: string;
}
PropTypeDescription
childrenReactNodePage content (header, filters, list, etc.).
classNamestring (optional)Extra Tailwind classes for the outer layout container.

ActivityFeedHeader

interface ActivityFeedHeaderProps {
title?: string;
description?: string;
query: string;
onQueryChange: (value: string) => void;
}
PropTypeDescription
titlestring (optional)Page title; defaults to "Activity Feed".
descriptionstring (optional)Short subtitle below the title.
querystringCurrent search query value.
onQueryChange(value: string) => voidCalled when the user types into the search input.

ActivityFeedFilters

interface ActivityFeedFiltersProps {
events: ActivityEvent[];
filter: ActivityFeedFilterState;
onFilterChange: (next: ActivityFeedFilterState) => void;
}
PropTypeDescription
eventsActivityEvent[]Full events list used to compute per-type counts.
filterActivityFeedFilterStateCurrent filter state (type + query).
onFilterChange(next: ActivityFeedFilterState) => voidCalled when the user clicks a type chip; query is preserved.

ActivityFeedList

interface ActivityFeedListProps {
events: ActivityEvent[];
timestampMode: TimestampMode;
now: Date;
}
PropTypeDescription
eventsActivityEvent[]Already-filtered and paged events; the list groups them by date label (Today, Yesterday, etc.).
timestampMode"relative" | "absolute"Controls timestamp rendering for each row.
nowDateReference timestamp used for computing relative times.

ActivityEventRow

interface ActivityEventRowProps {
event: ActivityEvent;
timestampMode: TimestampMode;
now: Date;
}
PropTypeDescription
eventActivityEventThe event to render.
timestampMode"relative" | "absolute"Controls how the row’s timestamp is rendered.
nowDateReference timestamp for relative calculations.

ActivityFeedPagination

interface ActivityFeedPaginationProps {
pagingMode: FeedPagingMode;
currentPage: number;
totalPages: number;
canLoadMore: boolean;
onPageChange: (page: number) => void;
onLoadMore: () => void;
}
PropTypeDescription
pagingMode"pagination" | "infinite"Determines whether a Pagination control or "Load more" pattern is rendered.
currentPagenumberCurrent page index (1-based) for pagination mode.
totalPagesnumberTotal page count for pagination mode.
canLoadMorebooleanWhether there are more events to load in infinite mode.
onPageChange(page: number) => voidCalled when the user selects a different page.
onLoadMore() => voidCalled when the user clicks "Load more" or when the sentinel enters view (infinite mode).

Usage Examples

Basic activity feed

<ActivityFeedPage
events={events}
/>

With controlled filter state

const [filter, setFilter] = useState<ActivityFeedFilterState>({
type: null,
query: "",
});

<ActivityFeedPage
events={events}
filterState={filter}
onFilterChange={setFilter}
pagingMode="pagination"
pageSize={10}
/>

Infinite "Load more" feed

<ActivityFeedPage
events={events}
pagingMode="infinite"
pageSize={20}
/>

Composable layout using building blocks

import React, { useCallback, useMemo, useState } from "react";
import {
ActivityFeedLayout,
ActivityFeedHeader,
ActivityFeedFilters,
ActivityFeedList,
ActivityFeedPagination,
type ActivityEvent,
type ActivityFeedFilterState,
} from "@ignix-ui/activity-feed-page";

function ComposableActivityFeed({ events }: { events: ActivityEvent[] }) {
const [filter, setFilter] = useState<ActivityFeedFilterState>({
type: null,
query: "",
});
const [page, setPage] = useState(1);
const pageSize = 10;

const sorted = useMemo(
() => [...events].sort((a, b) => b.occurredAt.getTime() - a.occurredAt.getTime()),
[events],
);

const filtered = useMemo(
() =>
sorted.filter((event) => {
if (filter.type && event.type !== filter.type) return false;
const q = filter.query.trim().toLowerCase();
if (!q) return true;
const text = [
event.title,
event.description,
event.actor.name,
event.actor.meta ?? "",
event.contextLabel ?? "",
]
.join(" ")
.toLowerCase();
return text.includes(q);
}),
[sorted, filter.type, filter.query],
);

const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
const pagedEvents = useMemo(() => {
const start = (page - 1) * pageSize;
return filtered.slice(start, start + pageSize);
}, [filtered, page, pageSize]);

const handleFilterChange = useCallback((next: ActivityFeedFilterState) => {
setFilter(next);
setPage(1);
}, []);

const handleQueryChange = useCallback((query: string) => {
setFilter((prev) => ({ ...prev, query }));
setPage(1);
}, []);

return (
<ActivityFeedLayout>
<ActivityFeedHeader
title="Activity Feed"
description="Track recent actions, system updates, and user activity."
query={filter.query}
onQueryChange={handleQueryChange}
/>
<ActivityFeedFilters
events={sorted}
filter={filter}
onFilterChange={handleFilterChange}
/>
<ActivityFeedList
events={pagedEvents}
timestampMode="relative"
now={new Date()}
/>
<ActivityFeedPagination
pagingMode="pagination"
currentPage={page}
totalPages={totalPages}
canLoadMore={false}
onPageChange={setPage}
onLoadMore={() => undefined}
/>
</ActivityFeedLayout>
);
}