shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 1

shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 1

In this article, I discuss how Blocks page is built on ui.shadcn.com. Blocks page has a lot of utilities used, hence I broke down this Blocks page analysis into 5 parts.

shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 1
shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 2 (Coming soon)
shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 3 (Coming soon)
shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 4 (Coming soon)
shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 5 (Coming soon)

In part 1, we will look at the following:

Where to find blocks page code in the shadcn-ui/ui repository?
getAllBlockIds function
_getAllBlocks function

These function further call other utility functions that will be explained in the other parts.

Where to find blocks page code in the shadcn-ui/ui repository?

blocks/page.tsx is where you will find Blocks page related code in the shadcn-ui/ui repository

Just because it has only 10 lines of code does not mean it is a simple page, there is a lot going on behind these lines, especially in the lib/blocks.ts, but don’t worry, we will understand the utility functions used in depth later in this article and other parts as well.

BlocksPage gets the blocks from a function named getAllBlockIds() which is imported from lib/blocks and these blocks are mapped with a BlockDisplay component that shows blocks on the Blocks page. Let’s find out what is in getAllBlockIds()

getAllBlockIds function

The below code snippet is picked from lib/blocks.ts

export async function getAllBlockIds(
style: Style[name] = DEFAULT_BLOCKS_STYLE
) {
const blocks = await _getAllBlocks(style)
return blocks.map((block) => block.name)
}

This code snippet is self explanatory, style parameter gets a default value DEFAULT_BLOCKS_STYLE because in the Blocks page, we call getAllBlockIds without any params as shown below:

const blocks = await getAllBlockIds()

But wait, what is the value in DEFAULT_BLOCKS_STYLE?

At line 14 in lib/blocks, you will find this below code:

const DEFAULT_BLOCKS_STYLE = “default” satisfies Style[“name”]

“default” satisfies Style[“name”], Style is from register/styles. I just admire the quality of Typescript written in this shadcn-ui/ui. So, _getAllBlocks gets called with a param named style that is initiated to “default”. So far, the code is straight forward. Let’s now understand what is in _getAllBlocks

_getAllBlocks function

The below code snippet is picked from lib/blocks.ts

async function _getAllBlocks(style: Style[name] = DEFAULT_BLOCKS_STYLE) {
const index = z.record(registryEntrySchema).parse(Index[style])

return Object.values(index).filter(
(block) => block.type === components:block
)
}

Even though, getAllBlockIds from above calls this function with a parameter, this function still has a default value set to the style parameter.

const index = z.record(registryEntrySchema).parse(Index[style])

Code above has the following:

z.record

Record schema in Zod are used to validate types such as Record. This is particularly useful for storing or caching items by ID.

registryEntrySchema

registryEntrySchema defines a schema for the blocks

export const registryEntrySchema = z.object({
name: z.string(),
description: z.string().optional(),
dependencies: z.array(z.string()).optional(),
devDependencies: z.array(z.string()).optional(),
registryDependencies: z.array(z.string()).optional(),
files: z.array(z.string()),
source: z.string().optional(),
type: z.enum([
components:ui,
components:component,
components:example,
components:block,
]),
category: z.string().optional(),
subcategory: z.string().optional(),
chunks: z.array(blockChunkSchema).optional(),
})

parse(Index[style])

parse is a schema method to check data is valid. If it is, a value is returned with full type information! Otherwise, an error is thrown.

Example:

const stringSchema = z.string();

stringSchema.parse(fish); // => returns “fish”
stringSchema.parse(12); // throws error

Index is imported from _registry_folder and contains all the components used in shadcn-ui/ui.

Looks like this file gets auto generated by scripts/build-registry.ts and this is also used in CLI package to add shadcn components into your project, more on this in the upcominhg articles.

Basically, we validate Index[“default”] against the registry schema to ensure the auto generated code is valid and is ready for further processing such as showing in blocks page.

_getAllBlocks filters the blocks based on the block type as shown below:

return Object.values(index).filter(
(block) => block.type === components:block
)

This is how you are able to see components that are specific to Blocks page.

Conclusion:

We looked at two important module functions named getAllBlockIds and _getAllBlocks. I find this code to be pretty self explanatory, I do admire the way zod’s Record schema validations are used on the auto generated registry index json.

Want to learn how to build shadcn-ui/ui from scratch? Check out build-from-scratch

About me:

Website: https://ramunarasinga.com/

Linkedin: https://www.linkedin.com/in/ramu-narasinga-189361128/

Github: https://github.com/Ramu-Narasinga

Email: ramu.narasinga@gmail.com

References:

https://github.com/shadcn-ui/ui/blob/main/apps/www/app/(app)/blocks/page.tsx

https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L20

https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L75

https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/schema.ts#L16

Please follow and like us:
Pin Share