Custom Portable Text
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Custom Portable Text is a specialized rendering component designed to handle Sanity's rich text format (Portable Text) within the Next.js application. It acts as a bridge between the structured JSON data stored in the Sanity Content Lake and the React components used to display that content on the frontend. By providing custom definitions for blocks, marks, and types, it ensures that content like images, links, and specialized "timelines" are rendered with consistent styling and functionality across the site.
The system is utilized extensively throughout the project to render project descriptions, page bodies, headers, and footer content. It supports not only standard text formatting but also complex nested objects such as custom image blocks and project-specific timeline sections, ensuring a high degree of visual fidelity and content flexibility.
Sources: components/CustomPortableText.tsx, app/(personal)/layout.tsx:64-70, sanity/schemas/documents/project.ts:88-142
Component Architecture
The CustomPortableText component wraps the standard PortableText component from next-sanity. It defines a components object which maps Sanity schema types to specific React functional components.
Data Flow
The following diagram illustrates how structured content flows from the Sanity schema through the rendering component.
The data flow ensures that every block of content is processed according to its specific type definition.
Sources: components/CustomPortableText.tsx:18-68, app/(personal)/projects/[slug]/page.tsx:143-151
Key Configurations and Props
The CustomPortableText component accepts a specific set of properties to maintain context and apply styles.
| Prop | Type | Description |
|---|---|---|
id | string | null | The unique identifier of the Sanity document. |
type | string | null | The schema type of the document (e.g., 'project', 'page'). |
path | PathSegment[] | The field path within the document for content source tracking. |
paragraphClasses | string | Optional CSS classes for standard paragraph tags. |
value | PortableTextBlock[] | The actual rich text data array from Sanity. |
Sources: components/CustomPortableText.tsx:10-17
Renderers and Custom Types
Marks and Links
The component provides a custom renderer for the link mark. It uses standard <a> tags with specific hover transitions and security attributes (rel="noreferrer noopener").
Sources: components/CustomPortableText.tsx:25-36
Custom Block Types
Specific complex objects are handled via the types property within the Portable Text configuration:
- Image Blocks: Rendered using a localized
ImageBoxcomponent. It includes support for custom aspect ratios (16/9) and captions displayed in a specialized font style. - Timeline Blocks: Integration with the
TimelineSection. This renderer handles deep path nesting by appending the block's_keyto the path array to support precise content editing.
Schema Integration
The custom renderers in the frontend correspond directly to definitions in the Sanity schema documents. For instance, the project and page documents define array fields of type block that specifically allow timeline objects and custom image objects with captions and alt text.
| Schema File | Field Name | Content Types Allowed |
|---|---|---|
project.ts | description | block, timeline, image |
page.ts | body | block, timeline, image |
home.ts | overview | block (limited decorators) |
Sources: sanity/schemas/documents/project.ts:88-142, sanity/schemas/documents/page.ts:50-101, sanity/schemas/singletons/home.ts:20-56
Implementation Example
Standard usage of the component involves passing the fetched content and document metadata to ensure proper rendering and potential visual editing support.
// From app/(personal)/projects/[slug]/page.tsx
{description && (
<CustomPortableText
id={data?._id || null}
type={data?._type || null}
path={['description']}
paragraphClasses="font-serif max-w-3xl text-xl text-gray-600"
value={description as any}
/>
)}
Sources: app/(personal)/projects/[slug]/page.tsx:143-151
Conclusion
The CustomPortableText module is a critical infrastructure component in this project. By centralizing the logic for translating structured rich text into styled React components, it maintains consistency across the portfolio and provides a robust way to extend content types—such as images and timelines—while preserving the standard flow of textual information.