Add a new UI component
Create new React components in the Next.js frontend. GDI uses Next.js 14 with server and client components.
In this guide
Component types in Next.js 14
Create the component file
Add interactivity
Use the component
Styling with TailwindCSS
Test your component
Component types in Next.js 14
Next.js 14 supports two types of components:
-
Server components (default)
- Render on the server
- Can directly access databases and APIs
- Better performance, smaller bundle sizes
- Cannot use browser APIs or React hooks like useState
-
Client components
- Marked with
'use client'directive - Can use interactivity and React hooks
- Access to browser APIs
- Run in both server and client
- Marked with
Create the component file
Create a new file in /components:
// components/dataset-card.tsx
interface DatasetCardProps {
title: string;
description: string;
publisher: string;
}
export function DatasetCard({
title,
description,
publisher,
}: DatasetCardProps) {
return (
<div className="border rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
<h3 className="text-lg font-semibold">{title}</h3>
<p className="text-gray-600 mt-2">{description}</p>
<p className="text-sm text-gray-500 mt-4">Publisher: {publisher}</p>
</div>
);
}
Add interactivity (if needed)
For interactive features, use 'use client':
"use client";
import { useState } from "react";
export function DatasetCard({ title, description }: DatasetCardProps) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div onClick={() => setIsExpanded(!isExpanded)}>
<h3>{title}</h3>
{isExpanded && <p>{description}</p>}
</div>
);
}
Use the component
Import and use in a page or other component:
import { DatasetCard } from "@/components/dataset-card";
export default function DatasetsPage() {
return (
<div>
<DatasetCard
title="Genomics Dataset"
description="Cancer research data"
publisher="Research Institute"
/>
</div>
);
}
Styling with TailwindCSS
GDI uses Tailwind for styling. Common patterns:
// Flexbox layout
<div className="flex items-center justify-between">
// Responsive design
<div className="w-full md:w-1/2 lg:w-1/3">
// Conditional styles
<button className={\`btn \${isActive ? 'bg-blue-500' : 'bg-gray-300'}\`}>
Test your component
Create a test file:
// __tests__/dataset-card.test.tsx
import { render, screen } from "@testing-library/react";
import { DatasetCard } from "@/components/dataset-card";
describe("DatasetCard", () => {
it("renders dataset information", () => {
render(<DatasetCard title="Test" description="Desc" publisher="Pub" />);
expect(screen.getByText("Test")).toBeInTheDocument();
});
});
Run tests:
npm test