Moving away from Oh-My-Zsh π
Introduction
Moving away from Oh-My-Zsh π
Introduction
I do software for my living, and for software development, I use tons of tools daily, like IntelliJ IDEA, PyCharm, and VS Code. One tool, however, that sits at the centre of almost everything I do is the terminal/shell(They are different, maybe a topic for another day). Some people might be wondering, why all the fuss, when you can use the terminal inside the IDE. Personal opinion, the integrated terminal in the IDE is shit. It is extremely uncomfortable to get things done. It is good for a couple of small commands, but for the most part, it is extremely uncomfortable. If you use it, try to move to a dedicated terminal emulator, you will thank me later.
Problem
I use iTerm2 emulator in my system. The default shell in macOS is ZSH. Most of the users will install Oh-My-Zsh to maximise the use of the terminal. It is a tool that will unlock the full potential of your shell. It will make you a power user. You will have an extreme level of customisations, including key bindings, tons of themes to choose from, and a huge source of community plugins. For someone getting serious about the terminal, Oh My Zsh (OMZ) feels like an obvious win and for a long time, I thought of it exactly that way.

First thing I did was to install iTerm2 and then OMZ. It was amazing to use for the first few weeks. I could change the theme anytime I want, have all the aliases I ever needed and much more. But after a few weeks, things started to get bad. My terminal startup time increased a lot. When I open a new tab, it takes forever to open. Initially, I thought, let me live with this, because of all the bells and whistles it brought. But slowly it became worse. Every time I opened a tab, it took 5 seconds or 6 seconds. That might not sound like a big deal in isolation. But guess what, when you are working with these shells, these small times count. It will break your rhythm and flow.

Solution
First Step
I wanted the features of OMZ, but I also wanted my shell to start fast. I also found that I didnβt use all the features of OMZ. It was hardly 30β50%. I wondered what I could do to make it faster. A couple of years back, I saw a YouTube video on how to move away from OMZ. One of the interesting things he mentioned was how everything is a simple script at the end of the day. Everything from terminal themes to key bindings was just a simple script.
I started by going through the Oh My Zsh GitHub repository and skimming the code to get a surface-level understanding of how things were structured. It wasnβt a deep analysis, but enough to realise that there was no hidden magic, just a collection of Zsh scripts being sourced during startup.
At that point, I decided to take matters into my own hands. I removed OMZ entirely from my system and began rebuilding my setup piece by piece. Instead of relying on OMZ as a framework, I manually sourced only the scripts I actually needed.
I picked the theme I liked, extracted the aliases I regularly used into a single file, and pulled in just the code required for features like autosuggestions. Everything else was left out.
At the end of it, I had a huge zshrc file. For context, .zshrc is the file that will be sourced when you open a new shell session. I felt the file was overly populated and was extremely large and difficult to maintain. Still, the problem wasnβt solved fully; my terminal sessions were still worse, not like 5β6 seconds, but at least in the ballpark of 4 seconds, because I was still doing the same thing that OMZ did.
Second Step
Before optimising the startup time, I wanted to clean up my zshrc. So I tried to break it into smaller chunks and keep it separate and reusable. I created a dedicated directory to hold all my Zsh-related scripts. The first and most obvious step was to move all my aliases into a separate file. I consolidated every alias I used into a single script and sourced it near the end of my .zshrc.
The next thing was to get the theme. I downloaded the official theme I needed from OMZ themes, created a separate script file for it, and sourced it. But it was not straightforward like aliases. It required some colour configs from OMZ and a few functions from it. To understand what was actually required, I went through the theme source line by line, removed the parts I didnβt need, and generated a theme from scratch. Since writing everything from scratch will be obsolete, as I might reuse basic theme configs in the future. I sourced the basic theme configs from OMZ, like colours. I kept each theme in a separate folder, thereby sourcing the theme I want dynamically based on a variable I keep in my zshrc file.
I still wanted a few plugins from OMZ, such as autosuggestions, autocompletions, and syntax highlighting, so I pulled only those plugins from the OMZ repository and sourced them manually in my .zshrc.
To make all of this work, I needed a few core helper functions that these plugins depended on at runtime. I tracked down the required helpers, grouped them into a dedicated folder, and sourced them explicitly.
At this point, I was sourcing multiple files. To keep things clean and scalable, I grouped related files and sourced them using loops.
.zsh_configs/
βββ helper/
β βββ completion.zsh
β βββ history.zsh
β βββ theme.zsh
βββ plugins/
β βββ zsh-autosuggestions/
β βββ zsh-completions/
β βββ zsh-syntax-highlighting/
βββ themes/
β βββ light-zsh/
β β βββ light-zsh.zsh-theme
β βββ old/
β βββ old.zsh-theme
βββ aliases.zsh
#### HELPERS ####
# Source all helper scripts under $ZSH_CONFIGS/helper
for helper in "$ZSH_CONFIGS"/helper/**/*.zsh; do
source "$helper"
done
#### PLUGINS ####
# Source all plugins under $ZSH_CONFIGS/plugins
for plugin in "$ZSH_CONFIGS"/plugins/**/*.plugin.zsh; do
source "$plugin"
done
fpath=("$ZSH_CONFIGS/plugins/zsh-completions/src" $fpath)
#### ALIASES ####
source "$ZSH_CONFIGS/aliases.zsh"
#### THEME ####
source "$ZSH_CONFIGS/themes/$ZSH_THEME/$ZSH_THEME.zsh-theme"
After all of this, my .zshrc looked much better. It was cleaner, easier to maintain, and far more readable. However, despite the refactor, I was still doing almost the same work that OMZ was doing. As a result, the startup time didnβt reduce drastically. It was slightly better, but still far from ideal.
Final Step
I started looking for places where I could improve the startup time. I used zprof to analyse the time taken by each process. Thereby, I found it was the plugins I used that took forever. The slowdown wasnβt caused by any single plugin or feature. Instead, most of the startup time was spent loading base infrastructure that many plugins depend on, things like completion initialisation (compinit), permission checks (compaudit), hook registration, and syntax-highlighting internals. Thatβs when I realised that I do not want every feature right before I write my first command. Loading everything together might be the bottleneck.
Instead, if I could load a bare-minimum shell first and defer the heavier plugins until after the prompt is ready, the overall experience would feel much faster.
This is when I came across zsh-defer. It allows you to defer the execution of Zsh code until the shell has finished starting up and is idle, essentially when itβs waiting for user input. It's just a small change I did, but the performance boost was massive.
for plugin in "$ZSH_CONFIGS"/plugins/**/*.plugin.zsh; do
zsh-defer source "$plugin"
done
Final Numbers
Before the optimisations, a new terminal tab startup was ~4β6 seconds. After the optimizations the effective startup time was ~110 ms, and the prompt appears almost instantly
Conclusion
There are still a few things I can improve. One obvious step is setting up a cron job to push my Zsh configuration directory to GitHub, which would make setting up a new system effortless. There are also a few other ideas and tools that could reduce the startup time even further. 110 ms is already fast, but itβs still possible to push it below 50 ms. I could have stuck with OMZ and tried to optimise it further, or even switched to something else entirely. But going through this process, understanding how everything works, and building my setup from scratch was genuinely fun.
λ©νλ°μ΄ν°
- post_id
- cc8b6bfc3b57
- slug
- moving-away-from-oh-my-zsh-cc8b6bfc3b57
- url
- https://medium.com/@vishwanathnarayanan29/moving-away-from-oh-my-zsh-cc8b6bfc3b57
- canonical_url
- https://medium.com/@vishwanathnarayanan29/moving-away-from-oh-my-zsh-cc8b6bfc3b57
- author_url
- https://medium.com/@vishwanathnarayanan29
- status
- ok
- fetched_at
- 2026-07-13 06:23:13