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<string, number>. 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 registryfolder 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