shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 5
Tags: javascript, nextjs, opensource, shadcnui
Published: 2024-06-21 20:44:22

In this article, I discuss how [Blocks page](https://ui.shadcn.com/blocks) is built on [ui.shadcn.com](http://ui.shadcn.com). [Blocks page](https://github.com/shadcn-ui/ui/blob/main/apps/www/app/(app)/blocks/page.tsx) has a lot of utilities used, hence I broke down this Blocks page analysis into 5 parts.

1.  [shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 1](https://medium.com/@ramu.narasinga_61050/shadcn-ui-ui-codebase-analysis-how-is-blocks-page-built-part-1-ac4472388f0a)
2.  [shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 2](https://medium.com/@ramu.narasinga_61050/shadcn-ui-ui-codebase-analysis-how-is-blocks-page-built-part-2-7714c8f36a43)
3.  [shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 3](https://medium.com/@ramu.narasinga_61050/shadcn-ui-ui-codebase-analysis-how-is-blocks-page-built-part-3-991c423b2ea3)
4.  [shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 4](https://medium.com/@ramu.narasinga_61050/shadcn-ui-ui-codebase-analysis-how-is-blocks-page-built-part-4-57c5eaa4916e)
5.  shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 5

This is final part 5 where I will discuss the following:

1.  getBlock function
2.  BlockPreview component
3.  BlockDisplay

getBlock function
-----------------

[getBlock function](https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27) uses that same function such as readFile, createTempSourceFile and project.createSourceFile. I explained about this in [great detail in part 4](https://medium.com/@ramu.narasinga_61050/shadcn-ui-ui-codebase-analysis-how-is-blocks-page-built-part-4-57c5eaa4916e).

To summarise, project.createSourceFile is an API provided by ts-morph to perform Typescript manipulations such as removing a variable from a file by access Typescript’s AST. This can simplify refactoring as it can save a lot of time in performing repetitive tasks such as renaming a property or function when dealing with Typescript code across a large code base.

```js
// source: https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27
export async function getBlock(
  name: string,
  style: Style\["name"\] = DEFAULT\_BLOCKS\_STYLE
) {
  const entry = Index\[style\]\[name\]

  const content = await \_getBlockContent(name, style)

  const chunks = await Promise.all(
    entry.chunks?.map(async (chunk: BlockChunk) => {
      const code = await readFile(chunk.file)

      const tempFile = await createTempSourceFile(\`${chunk.name}.tsx\`)
      const sourceFile = project.createSourceFile(tempFile, code, {
        scriptKind: ScriptKind.TSX,
      })

      sourceFile
        .getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
        .filter((node) => {
          return node.getAttribute("x-chunk") !== undefined
        })
        ?.map((component) => {
          component
            .getAttribute("x-chunk")
            ?.asKind(SyntaxKind.JsxAttribute)
            ?.remove()
        })

      return {
        ...chunk,
        code: sourceFile
          .getText()
          .replaceAll(\`@/registry/${style}/\`, "@/components/"),
      }
    })
  )

  return blockSchema.parse({
    style,
    highlightedCode: content.code ? await highlightCode(content.code) : "",
    ...entry,
    ...content,
    chunks,
    type: "components:block",
  })
}
```

This function attempts to remove nodes with “x-chunk” attribute. To my surprise, there’s some block examples that do contain this attribute as shown in the below image

![](https://media.licdn.com/dms/image/D5612AQH8OFiwfszw3Q/article-inline_image-shrink_1000_1488/0/1719002386250?e=1724284800&v=beta&t=mQx6HHCC7XZJv0x_KCXGWe-z1ZxIEbIRSlb3lRp2RCE)

and getBlock function returns the below object to a variable in BlockDisplay component.

```js
return blockSchema.parse({
    style,
    highlightedCode: content.code ? await highlightCode(content.code) : "",
    ...entry,
    ...content,
    chunks,
    type: "components:block",
  })
```

BlockDisplay Component
----------------------

Now that we fully understand what happens behind the scenes when you call getBlock as shown in the below code, [BlockDisplay](https://github.com/shadcn-ui/ui/blob/main/apps/www/components/block-display.tsx#L5) uses a Promise.all and waits till it gets all the blocks.

```js
export async function BlockDisplay({ name }: { name: string }) {
  const blocks = await Promise.all(
    styles.map(async (style) => {
      const block = await getBlock(name, style.name)
      const hasLiftMode = block?.chunks ? block?.chunks?.length > 0 : false

      // Cannot (and don't need to) pass to the client.
      delete block?.component
      delete block?.chunks

      return {
        ...block,
        hasLiftMode,
      }
    })
  )

  if (!blocks?.length) {
    return null
  }

  return blocks.map((block) => (
    <BlockPreview key={\`${block.style}-${block.name}\`} block={block} />
  ))
}
```

and then BlockPreview is used to show a block example.

BlockPreview component
----------------------

The below code is picked from BlockPreview component

```js
"use client"

import \* as React from "react"
import { ImperativePanelHandle } from "react-resizable-panels"

import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
import { useLiftMode } from "@/hooks/use-lift-mode"
import { BlockToolbar } from "@/components/block-toolbar"
import { Icons } from "@/components/icons"
import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
} from "@/registry/new-york/ui/resizable"
import { Tabs, TabsContent } from "@/registry/new-york/ui/tabs"
import { Block } from "@/registry/schema"

export function BlockPreview({
  block,
}: {
  block: Block & { hasLiftMode: boolean }
}) {
  const \[config\] = useConfig()
  const { isLiftMode } = useLiftMode(block.name)
  const \[isLoading, setIsLoading\] = React.useState(true)
  const ref = React.useRef<ImperativePanelHandle>(null)

  if (config.style !== block.style) {
    return null
  }

  return (
    <Tabs
      id={block.name}
      defaultValue="preview"
      className="relative grid w-full scroll-m-20 gap-4"
      style={
        {
          "--container-height": block.container?.height,
        } as React.CSSProperties
      }
    >
      <BlockToolbar block={block} resizablePanelRef={ref} />
      <TabsContent
        value="preview"
        className="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted"
      >
        <ResizablePanelGroup direction="horizontal" className="relative z-10">
          <ResizablePanel
            ref={ref}
            className={cn(
              "relative rounded-lg border bg-background",
              isLiftMode ? "border-border/50" : "border-border"
            )}
            defaultSize={100}
            minSize={30}
          >
            {isLoading ? (
              <div className="absolute inset-0 z-10 flex h-\[--container-height\] w-full items-center justify-center gap-2 text-sm text-muted-foreground">
                <Icons.spinner className="h-4 w-4 animate-spin" />
                Loading...
              </div>
            ) : null}
            <iframe
              src={\`/blocks/${block.style}/${block.name}\`}
              height={block.container?.height}
              className="chunk-mode relative z-20 w-full bg-background"
              onLoad={() => {
                setIsLoading(false)
              }}
            />
          </ResizablePanel>
          <ResizableHandle
            className={cn(
              "relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-\[6px\] after:-translate-y-1/2 after:translate-x-\[-1px\] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block",
              isLiftMode && "invisible"
            )}
          />
          <ResizablePanel defaultSize={0} minSize={0} />
        </ResizablePanelGroup>
      </TabsContent>
      <TabsContent value="code">
        <div
          data-rehype-pretty-code-fragment
          dangerouslySetInnerHTML={{ \_\_html: block.highlightedCode }}
          className="w-full overflow-hidden rounded-md \[&\_pre\]:my-0 \[&\_pre\]:h-\[--container-height\] \[&\_pre\]:overflow-auto \[&\_pre\]:whitespace-break-spaces \[&\_pre\]:p-6 \[&\_pre\]:font-mono \[&\_pre\]:text-sm \[&\_pre\]:leading-relaxed"
        />
      </TabsContent>
    </Tabs>
  )
}
```

As you can see, blocks are rendered using an iframe. An [example URL](https://ui.shadcn.com/blocks/default/authentication-04) is provided in the screenshot below.

![](https://media.licdn.com/dms/image/D5612AQGyJNWRhHLK7w/article-inline_image-shrink_1000_1488/0/1719002385253?e=1724284800&v=beta&t=v4dzI9NLPhDdXNh4lK3sHNVa_f3NP0nyyztx2UaHLVo)

Similarly, you can load other blocks by visiting their relevant URL.

Conclusion:
-----------

In these 5 parts, I studied the code used in building the Blocks page found on [ui.shadcn.com/blocks](http://ui.shadcn.com/blocks).

On this side of codebase, I have seen some advanced Typescript patterns such as using Records and parsing objects with zod to make sure they meet certain set schema standards. My favourite was using ts-morph to perform some variable removing operations on the code picked from a file using AST API (that sounds cool, lol) just so the code presented to the “client” component requires what’s needed and nothing more than that.

Frankly speaking, it was not easy to read and understand this code. In my next adventure, I will use this momentum to understand how the shadcn-ui/ui’s CLI package is built and write articles about this CLI package. It will be interesting to find out what happens under the hood when you type \`npx shadcn-ui add button\` for example.

> _Want to learn how to build shadcn-ui/ui from scratch? Check out_ [_build-from-scratch_](https://tthroo.com/)

About me:
---------

Website: [https://ramunarasinga.com/](https://ramunarasinga.com/)

Linkedin: [https://www.linkedin.com/in/ramu-narasinga-189361128/](https://www.linkedin.com/in/ramu-narasinga-189361128/)

Github: [https://github.com/Ramu-Narasinga](https://github.com/Ramu-Narasinga)

Email: [ramu.narasinga@gmail.com](mailto:ramu.narasinga@gmail.com)

References:
-----------

1.  [https://github.com/shadcn-ui/ui/blob/main/apps/www/components/block-display.tsx#L5](https://github.com/shadcn-ui/ui/blob/main/apps/www/components/block-display.tsx#L5)
2.  [https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27](https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27)