- - -

portfolio dev (part 1): latex and code

created on 2024-08-08

tags: [personal, portfolio-dev, coding]
- - -

as the school year comes in (im writing this in early august), im expecting to slow down my blog frequency (even though this is not guaranteed to be the case). with this in mind, i would like to start documenting my development process so that:


  • i dont lose track of what im doing and dont end myself over deprecated packages in a year or so
  • i can help other people understand how my blog works (and maybe even help them make their own one day)

this will be my first attempt at a more "formal" post - im hoping that this blog will have no glaring errors by the time i publish it. after all, my last few blogs represented more of a tangent than an actual blog...

i would like to express my gratitude to this guy named kevin for reading my blogs, checking my errors and (cough cough) breaking my models.

with this in mind, let's start diving into the code.


setting up nextjs

as of right now, i am using nextjs 14.2.5.

i set up my nextjs project the way the tutorial on their website described.

Loading...

here were the settings that i used:

Loading...

this will create a next app in a folder with your project's name on it.
i installed these packages for my blog, but not all of them are necessary (obviously).


  • next-mdx-remote - parsing remote .mdx files (honestly not necessary). i only use it for the mdxremote component.
  • gray-matter - parses frontmatter and content of .mdx files
  • dompurify - protects against xss attacks (specifically because i use html injection for code blocks)
  • husky - pre-commit and pre-push linting/formatting (make your code look good and readable)
  • rehype - adds classes to code tags to enable fancy code css

you should familiarize yourself with the directories and what they are meant for.

if you enter the directory of your project and run the command npm run dev, you should be able to view your website if you go to localhost:3000 in your browser (i believe this is the default port - if it is not 3000, the terminal will tell you which port to go to).


fig 1 - default next page after setup

next@14.2.5: default page


that's essentially it for the setup, but now it's time for the coding.

note: please keep in mind that i am using the app router.


the home page!

the app router works in a way such that each folder is a route. for example:


  • the page / renders the js file app/page.js
  • the page /blog renders the js file app/blog/page.js.
  • the page projects/[id] renders the js file app/projects/[id]/page.js (yes, this folder structure works for dynamic fetching as well!)

with that being said, my home page's source code (app/page.js) will be shown below!

Loading...

of course, im not gonna show all of it. if you want to see all of it, you can just go to my full source code (in the footer).

the important part here is the use of tailwind css! as you can see, the div and unordered list both have styling. the property name becomes "classname" (as opposed to "class" in html). now...what do the classes do?

the class "space-y-5" causes all the inner elements to be seperated vertically. it essentially just adds


margin-top: 1.25rem;


to the element. meanwhile, the "mx-6" sets the margin of each child element!

the great thing about tailwind is that the documentation is very easily readable. you can actually just search up "tailwind space between" and get the classnames required to do what you want!

here is the link to the docs!


tailwind docs - margin

look at these beautiful tailwind docs


now, you may notice that im missing a navbar. where is it???


layout.js and components

one of the great things about next (and react) is components. you can make custom js files that are both reusable and independent to the rest of the code where the component is used. for example, i have a few components:


  • navbar - to render the navigation bar at the top of my page
  • bloglist - to make lists in my blog (you are literally looking at it right now)
  • blogcode - to add code into my blog
  • latexwrapper - to add latex into my blog

in order to make a component, you just need a js file. i like to keep my stuff organized, so i have a folder: src/components. to give an example, ill show you my navbar code (src/components/navbar.js).

Loading...

the reason i have a duplicate link to each page is for responsiveness. if you read the tailwind css, you can see the word "hidden" in the class name. this is a tailwind class that hides the element on a certain screen size. in this case, the navbar is rendered differently on mobile screens.

it is important to note that tailwind is mobile-first. this means that the classes you write will be applied to all screen sizes and larger unless you specify otherwise.

if you would like to understand the css better, i highly recommend checking out the tailwind docs!

now...earlier, i mentioned that the navbar component was not in my home page's code. how did i add it? this is where layout comes in. this file (app/layout.js) is a wrapper for all of my pages. this is where i add my navbar and footer. everything else is rendered in the {children} prop.

Loading...

as you can see, the navbar component is rendered at the top of the page!

all in all, this becomes very useful when you have a lot of pages that need the same components. now...onto the next section!


the blog page

the blog page is a bit more complicated than the home page. this is because i have to parse the frontmatter and content of the .mdx files from a different directory.

all of my .mdx files are in the src/posts directory. the blog page (app/blog/page.js) is shown below.

Loading...

the important part here is the use of the getSortedPostsData() function. this function is in the src/lib/posts.js file. the "@" symbol is a shortcut to the "src" directory (we told the program not to change that shortcut when we initially set up the project).

since this file is not in the app router (src/app), it is not able to be accessed by the browser. this means that the function is only used in the server-side rendering of the page. this ended up causing some problems for me later, but we'll get to that when we cover it.

the function essentially reads all the .mdx files in the src/posts directory and returns them in a sorted array. it also gets all blog tags and returns them to be rendered.

Loading...

this function uses the fs and path modules to read the files in the directory. it then uses the gray-matter package to parse the frontmatter of the .mdx files.

the function also takes in a tag as an argument. if the tag is not empty, the function will only return posts with that tag. this version of the function is used to render blogs in the page /blog/[tag]/tag (filtered blogs by tag).
now, let's get into the actual blog post page! here is the code for the page app/blog/[id]/page.js (you're looking at this page right now)!

Loading...

the first part of my return statement simply renders the frontmatter of the .mdx file in a slightly nicer format.

the getPostFromId() function is in the same file as the getSortedPostsData() function. it reads the .mdx file with the id params.id and returns the content and frontmatter.

the MDXRemote component is used to render the content. the components prop is used to render custom components (the ones i made) in the content. this allows me to write something like

Loading...

in my .mdx files!

now, let's get to the interesting part: the process. most of the next section will be about the process of making the custom components - not the code itself. the tutorial basically ends here.


blog format and components

this part exists for the purpose of documenting my thought process.

i wanted to make my blog as readable as possible. this took me a long time to learn. originally, my blog was just a bunch of text and html that spread across the entire page. if you look at it now, it only takes up to a maximum of 56rem (896px) of the page width. this was for the sake of responsiveness - it simply looked out of place when viewing the original version of my blog on a mobile viewport. this was also due to the inspiration of other people's blogs, since most of them were also not a full page's width.


tumtarts blog

example: tumtarts blog - a blog that inspired me :D (source)


in the code shown earlier, you can see that the article tag has the class "min-w-0 max-w-4xl". this sets the minimum width of the article to 0 and the maximum width to 4xl.


for the most part, this helped my text stay readable. however, i also wanted to add a template for images, as it was getting painful to type out the html for each image. for example, this was the html behind my first image in my blog (insure 2024):

Loading...

i was getting quite tired of copy and pasting the same code over and over again. this is where the BlogImage component comes in. now, i am able to write this instead:

Loading...

(this is the code for the image above)
it makes things easier and more readable when i am reviewing my blogs.

now, let's talk about the latex. when i was writing my last blog (cat or car), i wanted to explain some of the basic math behind a neural network. however, i was not able to render it in a nice format without latex. i spent a lot of time trying to figure out how to use katex in my blog, but i was not able to get it to work. this is specifically because i needed the latex to render in the server-side rendering of the page (otherwise, i had to rewrite my lib functions that i mentioned earlier). after spending about 2 days, i found a much simpler answer - a math api.

the math api i used was the math api vercel app. by passing in a latex string, i was able to get a png image of the rendered latex. it was kind of a lazy workaround, but...it works.

after getting the image, i needed to render it - preferrably automatically - to scale, since not all latex expressions would be the same size. in order to do this seamlessly, i made the LatexWrapper component. this component takes in a latex string and renders the image in a way that scales with the text. it estimates the size of the image based on the length of the text i pass in.

Loading...

gives me this:


latex: \sum_{i=1}^{n} i = \frac{n(n%2B1)}{2}

cool! now i can write math in my blog. but...what about code?

originally, i just used the <code> tag to write code. however, it looked super ugly. in order to get syntax highlighting, i used the rehype package. this package adds classes to the code tags in the .mdx files. i then used the BlogCode component to render the code. this component takes in a language prop and a content prop.

thankfully, since the BlogCode component was a sub-component, i could render it on the client side. this meant that i could use the highlight.js package to render the code. i also used the file atom-one-dark.css to style the "classified" code (the import line is in the main blog page).

Loading...

the DOMPurify package is used to sanitize the code. this is to prevent cross-scripting xss attacks (code injection).

now, i can write code in my blog!

that's basically all i have so far, so let's jump to our final section:


conclusion + future work?

it's been a while since i've touched nextjs (or any javascript for that matter), so this was a nice refresher. i am also happy that i am blogging, since this is a way to document my progress!

there are a few things that i would like to do in the future:


  • add a search bar to my blog
  • add a pagination system to my blog (especially if i keep blogging at this frequency)
  • add a spotify api to my blog (maybe see what im listening to?)
  • further optimize my blog components - maybe create new ones on demand (maybe cooking related, since im trying to learn how to cook - blog coming soon?)

i hope you enjoyed this blog! i will be back soon with another one.

if you have anything you'd like to or ask about (or suggest improvements), feel free to reach out i always love talking to new people :)

that's it! byebye~