Collections
Collections are a way to organize and query file-system data in renoun. They are a powerful tool that allows you to define a schema for file exports and query those exports using a simple API.
Creating a Collection
Let’s walk through an example of creating a collection for a blog. We’ll define a PostsCollection
that targets all MDX files within the posts directory using the filePattern
field that accepts a minimatch pattern:
Configuring Collections
You can specify a baseDirectory
to trim the beginning of the generated source paths up to that point:
In this example, the PostsCollection
will target all MDX files within the posts
directory and generate paths relative to the posts
directory. This means a file system path like posts/how-to-build-a-button-component.mdx
will be transformed into the URL path /how-to-build-a-button-component
. Notice the baseDirectory
is trimmed from the source path.
If you want to customize the source path, you can set the basePath
option which will prepend a path to the final generated path:
The basePath
option is useful if the targeted directory is a different name then what you want for the route. Note, this option does not affect the path used in getSource
to query sources.
Collection Sources
Sources are an abstraction of the file system that represent a single file or directory in a collection. You can query sources for descendant sources, exports, siblings, and other metadata.
To retrieve a source, you can use the getSource
method with a path relative to the collection’s baseDirectory
:
File Export Types
To define the shape of the file exports, you can pass a generic type to the Collection
constructor. This type will be used to type-check the exports of the files in the collection.
Since we’re working with MDX, we can use the MDXContent
type from the renoun/mdx
package to define the shape of the default export. We’ll also add types for the frontmatter
export we expect to be defined in the MDX files:
This will ensure that the default
export of each MDX file in the posts
directory is of type MDXContent
, and that the frontmatter
export is an object with title
and description
properties.
Schema Validation
While types are useful for type-checking, you can also define a schema for the collection. A schema is a set of rules that define the structure of the file exports and provide useful error handling.
You can use the schema
field to define a schema for the expected file exports in a collection. Using a library like Zod can help you define schemas in a type-safe way:
import { Collection } from 'renoun/collections'
import type { MDXContent } from 'renoun/mdx'
import { z } from 'zod'
const frontmatterSchema = z.object({
title: z.string(),
description: z.string(),
})
interface PostsSchema {
default: MDXContent
frontmatter: z.infer<typeof frontmatterSchema>
}
export const PostsCollection = new Collection<PostsSchema>({
filePattern: '*.mdx',
baseDirectory: 'posts',
basePath: 'blog',
schema: {
frontmatter: frontmatterSchema.parse,
},
})
Now we’ll validate the front matter of each MDX file in the posts
directory using the frontmatterSchema
. This ensures that each file adheres to the schema and provides type safety when accessing the front matter.
See the Zod and Valibot guides for more information on how to use schemas with collections.
Querying Sources
Once you’ve configured your collections, you can query the targeted files using the getSource
and getSources
methods. These methods return Source
instances that represent a single file or directory in the collection.
To start, we’ll generate a set of navigation links for all of the posts in the PostsCollection
using the getSources
method. Each Source
has additional helper methods, such as getPath
that returns a URL-friendly path of the source relative to the collection’s base directory:
We can now generate a page for individual posts using a slug
parameter to retrieve the source for a specific MDX file in the posts
directory:
This was a simple example of how to query and render the contents of MDX files using collections. Collections are not limited to MDX files and can be used with any file type your bundler is capable of importing.
Composite Collections
In addition to creating individual collections, renoun also allows you to define composite collections. A composite collection is a combination of multiple collections, allowing you to treat them as a single entity. This can be useful when you want to query across different directories or file patterns while maintaining a unified interface.
Creating a Composite Collection
Let’s say you have two collections, one for blog posts, and another for components. Using a composite collection, you can combine these into a single collection that can be queried as if it were one:
import { Collection, CompositeCollection } from 'renoun/collections'
const PostsCollection = new Collection({
filePattern: '*.mdx',
baseDirectory: 'posts',
})
const ComponentsCollection = new Collection({
filePattern: '**/*.{ts,tsx}',
baseDirectory: 'src/components',
})
const AllCollections = new CompositeCollection(
PostsCollection,
ComponentsCollection
)
With this setup, AllCollections
allows you to query across both PostsCollection
and ComponentsCollection
seamlessly.
Querying Across Collections
When retrieving a source and querying for siblings, composite collections will account for all sources across the collections it comprises:
const source = AllCollections.getSource(
'posts/how-to-build-a-button-component'
)!
const [previousSource, nextSource] = await source.getSiblings()
Here, source.getSiblings()
will return the sources from both PostsCollection
and ComponentsCollection
as a combined set.
Narrowing Source Types
Collections provide a type guard to check if a source belongs to a specific collection within a composite collection. You can use the <Collection>.hasSource
method to safely narrow the type of a source when working with composite collections:
if (ComponentsCollection.hasSource(nextSource)) {
// nextSource is now typed as a ComponentsCollection source
}
This type guard ensures that you’re working with the correct source type within a composite collection, allowing you to access schema-specific exports.
Conclusion
Collections can be used to generate static pages, create navigations, site maps and much more. At their core, they abstract files and directories into either a CollectionSource
, FileSystemSource
, or ExportSource
allowing you to analyze and render them programmatically.
Explore more ways to utilize collections by visiting the recipes page for practical examples.
API Reference
FilePatterns
FilePatterns<Extension>
`${string}${Extension}`
`${string}${Extension}${string}`
BaseSource
BaseSource
Properties
getPath *
() => string
The full path to the source formatted to be URL-friendly, taking the
collection baseDirectory
and basePath
configuration into account.
getPathSegments *
() => string[]
An array of path segments to the source excluding the collection basePath
if configured.
getFileSystemPath *
() => string
The file path to the source in the file system.
getEditPath *
() => string
The path to the source on the local filesystem in development and the git repository in production if configured.
SourceProvider
SourceProvider<Exports>
Properties
getSource *
(path?: string | string[]) => Promise<FileSystemSource<Exports> | undefined>
Retrieves a source in the immediate directory or sub-directory by its path.
path
string | Array<string> | undefined
getSources *
<Depth extends number>(options?: { depth?: PositiveIntegerOrInfinity<Depth>; }) => Promise<FileSystemSource<Exports>[]>
Retrieves sources in the immediate directory and possibly sub-directories based on the provided depth
.
Defaults to a depth of Infinity
which will return all sources.
options
{ depth?: PositiveIntegerOrInfinity<Depth>; } | undefined
ExportSource
ExportSource<Value>
Properties
getName *
() => string
The name of the exported source. If the default export name cannot be derived, the file name will be used.
getType *
(filter?: SymbolFilter) => Promise<ResolvedType | undefined>
The resolved type of the exported source based on the TypeScript type if it exists.
filter
SymbolFilter | undefined
getTitle *
() => string
The name of the exported source formatted as a title.
getDescription *
() => string | undefined
The description of the exported source based on the JSDoc comment if it exists.
getTags *
() => { tagName: string; text?: string; }[] | undefined
The tags of the exported source based on the JSDoc comment if it exists.
getSlug *
() => string
The URL-friendly slug of the export name.
getText *
() => string
A text representation of the exported source if it is statically analyzable.
getValue *
() => Promise<Value>
The runtime value of the export loaded from the dynamic import generated at the related collection's call site. Note, any side-effects in modules of targeted files will be run.
getEnvironment *
() => "server" | "client" | "isomorphic" | "unknown"
The execution environment of the export source.
getPosition *
() => DeclarationPosition
The lines and columns where the export starts and ends.
getSiblings *
() => Promise<[previous?: ExportSource<Value>, next?: ExportSource<Value>]>
The previous and next export sources within the same file.
isMainExport *
() => boolean
Whether the export is considered the main export of the file based on the name matching the file name or directory name.
getPath *
() => string
The full path to the source formatted to be URL-friendly, taking the
collection baseDirectory
and basePath
configuration into account.
getPathSegments *
() => string[]
An array of path segments to the source excluding the collection basePath
if configured.
getFileSystemPath *
() => string
The file path to the source in the file system.
getEditPath *
() => string
The path to the source on the local filesystem in development and the git repository in production if configured.
FileSystemSource
FileSystemSource<Exports>
Properties
getName *
() => string
The base file name or directory name.
getTitle *
() => string
The file name formatted as a title.
getOrder *
() => string
Order of the source in the collection based on its position in the file system.
getDepth *
() => number
Depth of source starting from the collection.
getCreatedAt *
() => Promise<Date | undefined>
Date the source was first created.
getUpdatedAt *
() => Promise<Date | undefined>
Date the source was last updated.
getAuthors *
() => Promise<string[]>
Authors who have contributed to the source.
getSiblings *
(options?: { depth?: number; }) => Promise<[previous?: FileSystemSource<Exports>, next?: FileSystemSource<Exports>]>
The previous and next sources in the collection if they exist. Defaults to a depth of Infinity
which considers all descendants.
options
{ depth?: number; } | undefined
getExport *
<Name extends keyof Exports>(name: Name) => ExportSource<Exports[Name]>
A single named export source of the file.
name *
Name
getMainExport *
() => ExportSource<Exports[keyof Exports]> | undefined
The main export source of the file based on the file name or directory name.
getExports *
() => ExportSource<Exports[keyof Exports]>[]
All exported sources of the file.
isFile *
() => boolean
If the source is a file.
isDirectory *
() => boolean
If the source is a directory.
getPath *
() => string
The full path to the source formatted to be URL-friendly, taking the
collection baseDirectory
and basePath
configuration into account.
getPathSegments *
() => string[]
An array of path segments to the source excluding the collection basePath
if configured.
getFileSystemPath *
() => string
The file path to the source in the file system.
getEditPath *
() => string
The path to the source on the local filesystem in development and the git repository in production if configured.
getSource *
(path?: string | string[]) => Promise<FileSystemSource<Exports> | undefined>
Retrieves a source in the immediate directory or sub-directory by its path.
path
string | Array<string> | undefined
getSources *
<Depth extends number>(options?: { depth?: PositiveIntegerOrInfinity<Depth> | undefined; } | undefined) => Promise<Array<FileSystemSource<Exports>>>
Retrieves sources in the immediate directory and possibly sub-directories based on the provided depth
.
Defaults to a depth of Infinity
which will return all sources.
options
{ depth?: PositiveIntegerOrInfinity<Depth> | undefined; } | undefined
CollectionSource
CollectionSource<Exports>
Properties
getPath *
() => string
The full path to the source formatted to be URL-friendly, taking the
collection baseDirectory
and basePath
configuration into account.
getSource *
(path?: string | string[]) => Promise<FileSystemSource<Exports> | undefined>
Retrieves a source in the immediate directory or sub-directory by its path.
path
string | Array<string> | undefined
getSources *
<Depth extends number>(options?: { depth?: PositiveIntegerOrInfinity<Depth> | undefined; } | undefined) => Promise<Array<FileSystemSource<Exports>>>
Retrieves sources in the immediate directory and possibly sub-directories based on the provided depth
.
Defaults to a depth of Infinity
which will return all sources.
options
{ depth?: PositiveIntegerOrInfinity<Depth> | undefined; } | undefined
hasSource *
(source: FileSystemSource<any> | undefined) => source is FileSystemSource<Exports>
source *
FileSystemSource<any> | undefined
CollectionOptions
CollectionOptions<Exports>
Properties
filePattern *
string
The file pattern used to match source files. Accepts a minimatch file pattern.
baseDirectory
string | undefined
The base directory path to trim from calculated paths.
The path must match a portion of the base directory structure of the targeted file pattern.
For example, if the file pattern is src/components/*.{ts,tsx}
and the
baseDirectory
is src/components
, the path will only include the file name.
basePath
string | undefined
The base pathname used when calculating navigation paths. This includes everything after
the hostname (e.g. /docs
in https://renoun.com/docs
).
tsConfigFilePath
string | undefined
The path to the TypeScript config file.
filter
((source: FileSystemSource<Exports> | ExportSource<any>) => boolean) | undefined
A filter function to only include specific file system sources. If tsConfigFilePath
is defined,
all files matching paths in ignore
will always be filtered out.
sort
((a: FileSystemSource<Exports>, b: FileSystemSource<Exports>) => Promise<number>) | undefined
A custom sort function for ordering file system sources.
schema
{ [Name in keyof Exports]?: ((value: Exports[Name]) => Exports[Name]) | undefined; } | undefined
Validate and transform exported values from source files.
Collection
Collection<AllExports>
Creates a collection of file system sources based on a file pattern.
Constructors
<AllExports>(options: CollectionOptions<AllExports>, getImport?: GetImport | Array<GetImport> | undefined) => Collection<AllExports>
Methods
getPath
() => string
getDepth
() => number
getSource
(path?: string | string[]) => Promise<FileSystemSource<AllExports> | undefined>
getSources
({ depth }?: { depth?: number; }) => Promise<Array<FileSystemSource<AllExports>>>
hasSource
(source: FileSystemSource<any> | undefined) => source is FileSystemSource<AllExports>
Properties
options
CollectionOptions<AllExports>
CompositeCollection
CompositeCollection<Collections>
Combines multiple collections into a single source provider that can be queried together. This is useful for creating feeds or navigations that span multiple collections.
Constructors
<Collections extends Array<CollectionSource<any>>>(collections: Collections) => CompositeCollection<Collections>
Methods
getSource
(path?: string | string[]) => Promise<FileSystemSourceUnion<Collections> | undefined>
getSources
({ depth }?: { depth?: number; }) => Promise<FileSystemSourceUnion<Collections>[]>
isExportSource
(source: unknown) => source is ExportSource<any>
Parameters
source *
unknown
Returns
booleanisFileSystemSource
(source: unknown) => source is FileSystemSource<any>
Parameters
source *
unknown
Returns
booleanisCollectionSource
(source: unknown) => source is CollectionSource<any>
Parameters
source *
unknown