When working from my home office I like to use good quality products that make my working life more comfortable.
ℹ️ I change aspects of my setup fairly often so this page will serve as a living document and a place to point curious developers to when I get asked.
Software
IDE
VS Code (external site). I also use Zed (external site) from time to time.
I tend to switch my colour themes fairly often, but these are the most used:
- Pierre Dark (external site)
- Kanagawa Wave (external site)
- Rosé Pine Moon (external site)
- Catppuccin Mocha (external site)
- Rigel (external site) which I made, actually
I regularly switch the fonts I use too:
- Geist Mono (external site)
- Comic Code (external site) (as much as I hate Comic Sans, I happen to LOVE Comic Code for coding.. who knew!)
- Recursive Mono (external site)
- Berkeley Mono (external site)
A copy of my VS Code settings.json can be found here (external site) and a full list of my installed extensions can be found here (external site).
Extensions
- Surround (external site) a simple yet powerful extension to add wrapper templates around your code blocks. It has many built-in templates and you can add your own. I’ve added a few of my own templates to the settings below.
- Error Lens (external site)
- Multiple cursor case preserve (external site)
- Project Manager (external site)
- Rewrap (external site)
- Toggle Quotes (external site)
- Turbo Console Log (external site)
- Sublime Commands (external site)
- REST Client (external site)
Surround settings
{
"surround.custom": {
"JSXBox": {
"description": "<Box>",
"disabled": false,
"label": "box",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "<Box>$TM_SELECTED_TEXT</Box>"
},
"JSXContainer": {
"description": "<Container>",
"disabled": false,
"label": "container",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "<Container>$TM_SELECTED_TEXT</Container>"
},
"JSXFlex": {
"description": "<Flex>",
"disabled": false,
"label": "flex",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "<Flex>$TM_SELECTED_TEXT</Flex>"
},
"JSXGrid": {
"description": "<Grid>",
"disabled": false,
"label": "grid",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "<Grid>$TM_SELECTED_TEXT</Grid>"
},
"TSGeneric": {
"description": "Generic<>",
"disabled": false,
"label": "TS Generic",
"languageIds": ["typescript", "typescriptreact", "astro"],
"snippet": "$1<$TM_SELECTED_TEXT, $0>"
},
"console-log": {
"description": "console.log( ... )",
"disabled": false,
"label": "console.log",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "console.log(`$TM_SELECTED_TEXT`, $TM_SELECTED_TEXT)"
},
"css-var": {
"description": "var($)",
"disabled": false,
"label": "var",
"languageIds": ["css", "postcss", "javascriptreact", "typescriptreact"],
"snippet": "var($TM_SELECTED_TEXT)"
},
"describe": {
"description": "describe('${1:name}', () => { ... })",
"disabled": false,
"label": "describe",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"snippet": "describe('${1:name}', () => {\n\t$TM_SELECTED_TEXT\n})$0"
},
"errorBoundary": {
"description": "<ErrorBoundary>",
"disabled": false,
"label": "errorBoundary",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"snippet": "<ErrorBoundary>$TM_SELECTED_TEXT</ErrorBoundary>"
},
"es6StringSubstition": {
"description": "${...}",
"disabled": false,
"label": "es6StringSubstition",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "${$TM_SELECTED_TEXT}"
},
"es6StringSubstitionBackticks": {
"description": "`${...}`",
"disabled": false,
"label": "es6StringSubstitionBackticks",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "`${$TM_SELECTED_TEXT}$0`"
},
"function": {
"description": "$( ... )",
"disabled": false,
"label": "function",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "$1($TM_SELECTED_TEXT)$0"
},
"jsxConditional": {
"description": "{x && ( ... )}",
"disabled": false,
"label": "jsxConditional",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "{$0 && ($TM_SELECTED_TEXT)}"
},
"jsxConditional2": {
"description": "{x ? ( ... ) : null}",
"disabled": false,
"label": "better jsxConditional",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "{$0 ? ($TM_SELECTED_TEXT) : null}"
},
"markdown-link": {
"description": "[]( ... )",
"disabled": false,
"label": "Markdown link [url]",
"snippet": "[$0]($TM_SELECTED_TEXT)"
},
"markdown-link-alt": {
"description": "[ ... ]()",
"disabled": false,
"label": "Markdown link [text]",
"snippet": "[$TM_SELECTED_TEXT]($0)"
},
"number": {
"description": "Number( ... )",
"disabled": false,
"label": "Number",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "Number($TM_SELECTED_TEXT)"
},
"parse": {
"description": "JSON.parse( ... )",
"disabled": false,
"label": "JSON.parse",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "JSON.parse($TM_SELECTED_TEXT)$0"
},
"promise": {
"description": "Promise<...>",
"disabled": false,
"label": "Promise<...>",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro"
],
"snippet": "Promise<$TM_SELECTED_TEXT>"
},
"reactForwardRef": {
"description": "forwardRef",
"disabled": false,
"label": "forwardRef",
"languageIds": ["javascriptreact", "typescriptreact"],
"snippet": "forwardRef<$1>($TM_SELECTED_TEXT)"
},
"reactFragment": {
"description": "<Fragment>",
"disabled": false,
"label": "fragment",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "<Fragment>$TM_SELECTED_TEXT</Fragment>"
},
"reactFragment2": {
"description": "<>",
"disabled": false,
"label": "fragment (simple)",
"languageIds": ["javascriptreact", "typescriptreact", "astro"],
"snippet": "<>$TM_SELECTED_TEXT</>"
},
"reactUseCallback": {
"description": "useCallback",
"disabled": false,
"label": "useCallback",
"languageIds": ["javascriptreact", "typescriptreact"],
"snippet": "useCallback(() => $TM_SELECTED_TEXT, [])"
},
"reactUseMemo": {
"description": "useMemo",
"disabled": false,
"label": "useMemo",
"languageIds": ["javascriptreact", "typescriptreact"],
"snippet": "useMemo(() => $TM_SELECTED_TEXT, [])"
},
"rem-polished": {
"description": "${rem($)}",
"disabled": false,
"label": "rem",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"snippet": "${rem($TM_SELECTED_TEXT)}"
},
"stringify": {
"description": "JSON.stringify( ... )",
"disabled": false,
"label": "JSON.stringify",
"languageIds": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"svelte",
"html",
"astro"
],
"snippet": "JSON.stringify($TM_SELECTED_TEXT)$0"
},
"suspense": {
"description": "<Suspense>",
"disabled": false,
"label": "suspense",
"languageIds": ["javascriptreact", "typescriptreact"],
"snippet": "<Suspense fallback={<Loader />}>$TM_SELECTED_TEXT</Suspense>"
},
"svelteIf": {
"description": "{#if}{/if}",
"disabled": false,
"label": "{#if}",
"languageIds": ["svelte"],
"snippet": "{#if $1}$TM_SELECTED_TEXT{/if}"
},
"svelteIfElse": {
"description": "{#if}{:else}{/if}",
"disabled": false,
"label": "{#if}{:else}",
"languageIds": ["svelte"],
"snippet": "{#if $1}$TM_SELECTED_TEXT{:else}$2{/if}"
}
}
}
Browser
Phi Browser (external site) has usurped Arc (external site) recently. I have also tried Zen (external site) which is excellent.
AI
For coding and agentic development, I use Claude (external site). When I add AI features to the apps I build, I usually use OpenAI (external site) models, mostly because they cost less.
To run my coding agents, I use Orca (external site). It runs each agent in parallel in its own git worktree, with terminals, diffs and a browser in one app. I really like it, and I love that it has a mobile app so I can keep my agents going when I’m away from my desk. Before Orca I used herdr (external site), and before that cmux (external site).
For automated pull request reviews, I use cubic (external site).
To give me better insight of my usage, I use OpenUsage (external site), a fantastic tool that gives me a breakdown of my usage across all the AI tools I use.
Raycast (external site)
I use it in nearly every aspect of my work. I use it for searching the web with its custom web searches (e.g. npm, JIRA, bundlephobia, MDN and many more); searching npm with the npm extension (external site) (that I created), and loads more. It truly is a massive productivity booster.
FYI I also created the GitHub Stars extension (external site).
- Toothpick (external site) - Manage Bluetooth connections
- JIRA (external site) - Necessary evil to interact with JIRA
- Set Audio Device (external site) - Set the input/output audio device
Terminal
I use Ghostty (external site).
Instead of bash/zsh, I use Fish Shell (external site) and the excellent TIDE (external site) prompt.
I also use a few other CLI apps to make the experience better, in particular, ni (external site) by Anthony Fu (external site) and Worktrunk (external site). “ni” is great for installing npm packages and running npm scripts. I bounce from project to project a lot and they often use different package managers. This tool makes it easy to install packages and run scripts without having to remember which package manager to use. Worktrunk takes the pain out of creating, switching and cleaning up git worktrees.
1Password (external site)
I never need to worry about passwords anymore. It integrates with my browser (via their extension) and it copies one-time passwords to the clipboard automatically. It’s also great on the iPhone.
Obsidian (external site)
I moved to Obsidian a while back and haven’t looked back.
And the rest:
- NextDNS (external site): A cloud-based private DNS service that gives you full control over what is allowed and what is blocked on the internet. There are Mac and iPhone apps that essentially block ads, malicious code and more.
- DevUtils (external site): This app is a recent addition and is an indispensable tool for my workflow.
- Granola (external site): AI meeting notes. It transcribes calls and turns my rough notes into something actually useful afterwards.
- Vorssaint (external site): A free, open-source menu bar app with hundreds of useful tools. You install only the ones you want. It replaced Vivid for me, because it also does extra brightness on XDR displays.
- MacShot (external site): A free, open-source app for screenshots and screen recordings, with annotation, blur, scrolling capture and text extraction built in.
- System Stats (external site): Real-time monitoring for my machines, containers and endpoints on a single dashboard.
- CaskHub (external site) and Homebrew’s official Mac app (external site): I use these to manage my apps and Homebrew formulae, so I don’t need the terminal for it.
- AudioSwitcher (external site): A menu bar app for quickly switching between all available input and output devices.
This page is featured on uses.tech (external site). If you’re reading this and want to see others like it, head on over there to find out more.
Hardware
Computer
14” Apple M1 Max MacBook Pro 💻. I love this little thing and it’s still going strong.
Monitor
MSI Prestige PS341WU Creator Monitor 34” (external site). Great wide-screen monitor without being too pricey.
Keyboard
Keychron Q1 version 2 (external site). I bought the barebones ISO version, Gateron Oil King switches and PBTFans Resonance keycaps from Mech Mods (external site). What’s not to love?
Mouse
The Logitech MX Master 4 (external site) is great, but if you have the MX Master 3S and it’s still working well, I wouldn’t bother upgrading. Instead of Logitech Options+, I use OpenLogi (external site) to remap the buttons and set the DPI. It’s free, needs no account and has no telemetry.
Desk and chair
Autonomous Smart Desk 2 (external site). An amazing and affordable standing desk. For the chair, it’s the Herman Miller Aeron (external site) — you need to invest in a good chair if you’re going to be sitting in it for hours on end.