shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 4
Tags: typescript, javascript, opensource, nextjs
Published: 2024-06-20 21:06: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
5.  shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 5 (Coming soon)

In part 3, I mentioned that I would provide an example that demonstrates ts-morph usage in shadcn-ui/ui. I made efforts to create this [ts-morph-usage-in-shadcn-ui](https://github.com/Ramu-Narasinga/ts-morph-usage-in-shadcn-ui) repository and put in the minimum required files to log the response of execution flow when \_getBlockContent is called.

In this article, I will discuss the functions \_extractVariable and project.createSourceFile.

\_extractVariable
-----------------

\_extractVariable function contains the below code:

```js
function \_extractVariable(sourceFile: SourceFile, name: string) {
  const variable = sourceFile.getVariableDeclaration(name)
  if (!variable) {
    return null
  }

  const value = variable
    .getInitializerIfKindOrThrow(SyntaxKind.StringLiteral)
    .getLiteralValue()

  variable.remove()

  return value
}
```

In order to understand this API, I had to read [ts-morph documentation for variables](https://ts-morph.com/details/variables). To explain in simple terms, ts-morph can be used to manipulate Javascript and Typescript code and comes handy when you are trying refactor a large code base. This [Refactoring-Typescript-code-with-ts-morph](https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/) has some great examples to demonstrate ts-morph power.

```js
// Extract meta.
const description = \_extractVariable(sourceFile, "description")
const iframeHeight = \_extractVariable(sourceFile, "iframeHeight")
const containerClassName = \_extractVariable(sourceFile, "containerClassName")
```

These variables can be found in [_registry_](https://github.com/shadcn-ui/ui/blob/main/apps/www/__registry__/new-york/block/authentication-04.tsx)[/new-york/authentication-04.tsx](https://github.com/shadcn-ui/ui/blob/main/apps/www/__registry__/new-york/block/authentication-04.tsx) example. This code basically removes the variables declared from the authentication-04.tsx but keeps the other typescript code.

Since there are a lot of files in _registry_ and they could involve this sort of extraction (aka Typescript code manipulation), using ts-morph sounds like the right choice here.

[The image below points to ts-ast-viewer, uploaded with authentication-04.tsx code](https://ts-ast-viewer.com/#code/JYWwDg9gTgLgBASRAQwOYFM4DMoRHAIgDt0APGAelDXQIChRJY4AZYIga2132LMoA27DvQbho8AN5wAQgFcYMCETgBfbnkIABClHSpgAZxhQAnhRIB3ALSnoHCnOAUARgqVF6jCXGkIiYApqGrw6egbGZhboNnZQDk5UAQpe4szSLMgu6ALBOJoEYfpGJuZWtvaOzgJZOaJkTPAAxsrGcAAm6IZNUMBgMMDKcAC8dHCEAIJwAhAGKmA0cJbAMAAWcDCWEHAtAnIgRIYAdHAAKquYWMBQbbv7KqvIhhsX07Ps2ND4y2tw6CjAXLIIjtOALQyGLZQdonc7oPQAcmeyDgADFoKgIPA7HIoGCnpD7NNhHBgaCUUJOBttoZgKgVHIwHBgFg4DiOtsiFi4I8AG6YYGkpotOREGCw16GdAtEE7CB7A48p6kuX8vHUDBHeqkRpyw7wFlQZAgdAACXQdNW8GGhAAHAAGe1gUja3Uy27KGDIdjwgDCNQhADljZgbQQbFg5AJcqtrJHo2DrAAWaaoABcYGs9tdPk6WGQUfgkaITQGQwAIk9Vi4IMhoQAKACUvjGcD0MFxKnrrfGAB52sBeTsA4ZgybhuG41HcgJ06heqDZ2mQOxrLGANoANkdzoAuqm0-PgO1rLtDNYAExwUgCZerjcOp2kXcEAB8PfGcH7g+HBLH6AnLABDIZkYH+c8mnQMV4TgAArORjBZUxTygsC8TAZCAEYLzfD9Py-AchyaEd-wnEBSGsAslDgI9QRsdcAGYAFYn33VBkEzTdcPwniCJ-Yi-xDCdaJojjLw2fgUOgqBuN4nje1WTDfyDISCDA8hrAYm9PjFawawEdo3xYd4iF7ChFPfOT5KZASVPHNTJJcZAahLTB1JgawQAUdATywaB9FwUVDMsqyeIAUWktkIFxP4AVybIZksak3jmZKcTxZBhWisU8LksywBCqyzMIwq8sI5TR1UkT2MzJNZNCviiJIqqF1EzMcNKhre0yBKeRgEABHRKAJ3+b0BDfMK4rMnqck60Le38QIYFyhrjxGuL6Aa3iYFMMAAIIUbAU2rb8LAGpINWeVOmGggQC0MhjTO9AjhaEBjpO8Y9AARycPR2hWuSKDm3jisHYH5PK2zKvs6qxI6gGQch5r7KAkCVnAqS0Pqj7utqGN+sG6AJ3BQloTfAAFAkoXaaa8fBoq2E4BHQtWPQsAnCg-KgTEPJJ6n3o+8YodI26BEohRtnYSl0D0mYmi4dzrEMfAgvhaWBZO+nQqGnmopivnoHaAB+ZmQYoRmOC1-DQd5K3PwW5IDXaYmqcNggNl2-aDbJtt0B+64fLgIHTa-CgSpD3t5EUIYdr2idDDkFwVxgd3hdUiNp2xhrjLmCOKCjjw7cj9whl5OtgGBGAJ2imB1YqkWM+jLPQpzj4fnWABxCBZmAvOC+ULWbcHpHBPskAPJTRXIMixXleb3jy2UAAyDiIEMABueA+QFFRMpFMUjckd2CFUCOLZ5NmJwAYlT5H9tVqB1bt8YAGU6QZMA84twew7BgGh9ygAxG-E74TlWMeToKgXCoE8t5Rc6YXByxEODBaKAMDM0MFAJoHMzqZXQJdAy8IjiGF5KgDWPFnJVwIEgGg5D8LLHaGsCcmEACcF5szMwuJaKhmF7QOjoZ+NO9lYzxlyI3XIEAXCwWlB5FoaoOh1g4GmFwvRUBWhIBCaw657RHAvPudoijDxGlMN0ZytAAbBzNuHa2v9batkbHQVQQA)

![](https://media.licdn.com/dms/image/D5612AQERZqSPxLVT7Q/article-inline_image-shrink_1000_1488/0/1718917265140?e=1724284800&v=beta&t=2yaigv1xYWzjjHcOf_rn4qjhV3H0wrpwKHDPJPDzn3Y)

In order to get access to TypeSscript AST API, it is now apparent that we need SourceFile API provided ts-morph

project.createSourceFile
------------------------

I found this [createSourceFile API reference in the ts-morph documentation](https://ts-morph.com/setup/adding-source-files#by-structure). It all makes sense now.

```js
const raw = await \_getBlockCode(name, style)

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

![](https://media.licdn.com/dms/image/D5612AQHkfpHCf5deDA/article-inline_image-shrink_1000_1488/0/1718917264083?e=1724284800&v=beta&t=RofLOPrrMdHR-mlcdCzHrUN7lpdGNt2Nm2Rf2fSNY0A)

To get access to AST API, we create a project with a source file and code so we could perform additional operations such as removing variables (like we saw in \_extractVariableNames). Because blocks examples have some additional variables that needed to removed from the original file, example: [authentication-01.tsx](https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/new-york/block/authentication-01.tsx) has some variables too and these variables are used in “Blocks” page to set iframe height and parent class name. This is a very useful technique when dealing with multiple files that require some form code formatting and variable extractions.

This is what \_getBlockContent responds with, when called in [getBlock](https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27). We can now move on to understand the code inside getBlock, which is used BlockDisplay component by providing the getBlock result into BlockPreview

![](https://media.licdn.com/dms/image/D5612AQFWWy-jFChn2A/article-inline_image-shrink_1000_1488/0/1718917264845?e=1724284800&v=beta&t=elW2V6ozzKs845Mn8fE9lrVzKjtdWAnP-lgBHIQXL7o)

In the final part 5, I will discuss the getBlock function, [BlockDisplay](https://github.com/shadcn-ui/ui/blob/main/apps/www/components/block-display.tsx#L5) and [BlockPreview](https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27) components and that brings this journey to understand how “Blocks” page is built to an end.

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

In order to understand the API used in \_extractVariable function in shadcn-ui/ui, I had to read [ts-morph documentation for variables](https://ts-morph.com/details/variables). To explain in simple terms, ts-morph can be used to manipulate Javascript and Typescript code and comes handy when you are trying refactor a large code base. This [Refactoring-Typescript-code-with-ts-morph](https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/)([https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/](https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/)) has some great examples to demonstrate ts-morph power.

To understand project.createSourceFile, I also had to setup a Github repository ([https://github.com/Ramu-Narasinga/ts-morph-usage-in-shadcn-ui](https://github.com/Ramu-Narasinga/ts-morph-usage-in-shadcn-ui)) and write the minimal code required to set up the ts-morph related functions used in lib/blocks.ts. To access AST API, we create a project with a source file and some code extracted from a file so we could perform additional operations such as removing variables (like we saw in \_extractVariableNames). Because blocks examples have some additional variables that needed to removed from the original file, example: [authentication-01.tsx](https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/new-york/block/authentication-01.tsx) ([https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/new-york/block/authentication-01.tsx](https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/new-york/block/authentication-01.tsx)) has some variables too and these variables are used in “Blocks” page to set iframe height and parent class names. This is a very useful technique when dealing with multiple files that require some form code formatting and variable extractions. I have to admit, this is some advanced form of Typescript usage that I have never seen or used before.

In the final part 5, I will discuss the getBlock function, [BlockDisplay](https://github.com/shadcn-ui/ui/blob/main/apps/www/components/block-display.tsx#L5) and [BlockPreview](https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27) components and that brings this journey to understand how “Blocks” page is built to an end.

> _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)
3.  [https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L107](https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L107)
4.  [https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/](https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/)
5.  [https://ts-ast-viewer.com/](https://ts-ast-viewer.com/)
6.  [https://mariodante.medium.com/unveiling-the-power-of-abstract-syntax-trees-ast-and-typescript-973321947bbc](https://mariodante.medium.com/unveiling-the-power-of-abstract-syntax-trees-ast-and-typescript-973321947bbc)
7.  [https://www.satellytes.com/blog/post/typescript-ast-type-checker/](https://www.satellytes.com/blog/post/typescript-ast-type-checker/)