diff --git a/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx
new file mode 100644
index 00000000..6474ac41
--- /dev/null
+++ b/app/[locale]/(user)/collaboratives/CollaborativesListingClient.tsx
@@ -0,0 +1,400 @@
+'use client';
+
+import BreadCrumbs from '@/components/BreadCrumbs';
+import { Icons } from '@/components/icons';
+import JsonLd from '@/components/JsonLd';
+import { Loading } from '@/components/loading';
+import { graphql } from '@/gql';
+import { TypeCollaborative } from '@/gql/generated/graphql';
+import { GraphQLPublic } from '@/lib/api';
+import { formatDate, generateJsonLd } from '@/lib/utils';
+import { useQuery } from '@tanstack/react-query';
+import Image from 'next/image';
+import { Button, Card, Icon, SearchInput, Select, Text } from 'opub-ui';
+import { useState } from 'react';
+import { cn } from '@/lib/utils';
+import Styles from '../datasets/dataset.module.scss';
+
+// Helper function to strip markdown and HTML tags for card preview
+const stripMarkdown = (markdown: string): string => {
+ if (!markdown) return '';
+ return markdown
+ // Remove code blocks first (before other replacements)
+ .replace(/```[\s\S]*?```/g, '')
+ // Remove inline code
+ .replace(/`([^`]+)`/g, '$1')
+ // Remove images
+ .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
+ // Remove links
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
+ // Remove headers
+ .replace(/^#{1,6}\s+/gm, '')
+ // Remove bold
+ .replace(/\*\*([^*]+)\*\*/g, '$1')
+ .replace(/__([^_]+)__/g, '$1')
+ // Remove italic
+ .replace(/\*([^*]+)\*/g, '$1')
+ .replace(/_([^_]+)_/g, '$1')
+ // Remove strikethrough
+ .replace(/~~([^~]+)~~/g, '$1')
+ // Remove blockquotes
+ .replace(/^\s*>\s+/gm, '')
+ // Remove horizontal rules
+ .replace(/^(-{3,}|_{3,}|\*{3,})$/gm, '')
+ // Remove list markers
+ .replace(/^\s*[-*+]\s+/gm, '')
+ .replace(/^\s*\d+\.\s+/gm, '')
+ // Remove HTML tags
+ .replace(/<[^>]*>/g, '')
+ // Remove extra whitespace and newlines
+ .replace(/\n\s*\n/g, '\n')
+ .replace(/\n/g, ' ')
+ .replace(/\s+/g, ' ')
+ .trim();
+};
+
+const PublishedCollaboratives = graphql(`
+ query PublishedCollaboratives {
+ publishedCollaboratives {
+ id
+ title
+ summary
+ slug
+ created
+ startedOn
+ completedOn
+ status
+ isIndividualCollaborative
+ user {
+ fullName
+ id
+ profilePicture {
+ url
+ }
+ }
+ organization {
+ name
+ slug
+ id
+ logo {
+ url
+ }
+ }
+ logo {
+ name
+ path
+ }
+ tags {
+ id
+ value
+ }
+ sectors {
+ id
+ name
+ }
+ sdgs {
+ id
+ code
+ name
+ }
+ geographies {
+ id
+ name
+ }
+ datasetCount
+ metadata {
+ metadataItem {
+ id
+ label
+ dataType
+ }
+ id
+ value
+ }
+ }
+ }
+`);
+
+const CollaborativesListingClient = () => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [sortBy, setSortBy] = useState('title_asc');
+
+ const {
+ data: collaborativesData,
+ isLoading,
+ error,
+ } = useQuery<{ publishedCollaboratives: TypeCollaborative[] }>(
+ ['fetch_published_collaboratives'],
+ async () => {
+ console.log('Fetching collaboratives...');
+ try {
+ // @ts-expect-error - Query has no variables
+ const result = await GraphQLPublic(
+ PublishedCollaboratives as any,
+ {}
+ );
+ console.log('Collaboratives result:', result);
+ return result as { publishedCollaboratives: TypeCollaborative[] };
+ } catch (err) {
+ console.error('Error fetching collaboratives:', err);
+ throw err;
+ }
+ },
+ {
+ refetchOnMount: true,
+ refetchOnReconnect: true,
+ retry: (failureCount) => {
+ return failureCount < 3;
+ },
+ }
+ );
+
+ const collaboratives = collaborativesData?.publishedCollaboratives || [];
+
+ // Filter and sort collaboratives
+ const filteredAndSortedCollaboratives = collaboratives
+ .filter((collaborative) => {
+ const matchesSearch = collaborative.title?.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ collaborative.summary?.toLowerCase().includes(searchTerm.toLowerCase());
+ return matchesSearch;
+ })
+ .sort((a, b) => {
+ const [field, direction] = sortBy.split('_');
+
+ if (field === 'title') {
+ const comparison = (a.title || '').localeCompare(b.title || '');
+ return direction === 'asc' ? comparison : -comparison;
+ } else if (field === 'startedOn') {
+ const dateA = new Date(a.startedOn || 0).getTime();
+ const dateB = new Date(b.startedOn || 0).getTime();
+ return direction === 'asc' ? dateA - dateB : dateB - dateA;
+ } else if (field === 'datasetCount') {
+ const countA = a.datasetCount || 0;
+ const countB = b.datasetCount || 0;
+ return direction === 'asc' ? countA - countB : countB - countA;
+ }
+ return 0;
+ });
+ const jsonLd = generateJsonLd({
+ '@context': 'https://schema.org',
+ '@type': 'WebPage',
+ name: 'CivicDataLab',
+ url: `${process.env.NEXT_PUBLIC_PLATFORM_URL}/collaboratives`,
+ description:
+ 'Explore collaborative data initiatives and partnerships that bring organizations together to create impactful solutions.',
+ });
+ return (
+