diff --git a/posts/first-draft.mdx b/posts/first-draft.mdx
deleted file mode 100644
index 8dc5fe0..0000000
--- a/posts/first-draft.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-slug: first-draft
-title: A Draft
-description: hi
-date: "2024-11-30"
-draft: true
----
-
-> A really long quote
diff --git a/posts/nuqs-pages.mdx b/posts/nuqs-pages.mdx
new file mode 100644
index 0000000..344c882
--- /dev/null
+++ b/posts/nuqs-pages.mdx
@@ -0,0 +1,152 @@
+---
+title: A Page of Query Params with nuqs
+description: how to organize query states by route
+date: "2024-11-30"
+draft: true
+---
+
+If you've seen code like this before, you'd probably appreciate [nuqs](https://nuqs.47ng.com/).
+
+```tsx
+const HomePage = () => {
+ const router = useRouter();
+ const [number, setNumber] = useState(0);
+
+ useEffect(() => {
+ setNumber(Number(router.query.number));
+ }, [router.query.number]);
+
+ return (
+
+
Home Page
+
The number is {number}
+
+
+ );
+};
+```
+
+`useRouter` and now `useSearchParams` from next are fine for the simple demo, but when it comes to building large apps that meaningfully use the search params as state, it's not enough. If you're not fully sold, but maybe a little query param curious, I'd highly recommend François' post about the library [here](https://francoisbest.com/posts/2023/storing-react-state-in-the-url-with-nextjs).
+
+## Organizing useQueryState in Shared Hooks
+
+When you start using nuqs, you may start out with something like this:
+
+```tsx
+const HomePage = () => {
+ const [number, setNumber] = useQueryState(
+ "number",
+ parseAsNumber.withDefault(0)
+ );
+
+ return (
+
+
Home Page
+
The number is {number}
+
+
+ );
+};
+```
+
+This is another great demo and shows the power of the library very efficiently. But if you've experienced code with `useState`'s everywhere, it's very easy to see how this could get out of hand quickly in a large codebase. The power with this library makes it very easy to use url state in a typesafe app which is very useful when used correct, but dangerous at the same time. URL search param key collisions are now easier, misuse by prop drilling the value and setter from `useQueryState` is easier, and whatever else your LLM could imagine when it sees the api similarity with `useState` and `useQuery`.
+
+So you may want to abstract `useQueryState` uses into exported hooks like this:
+
+```tsx
+import { useQueryState, parseAsNumber } from "nuqs";
+
+export const useNumberState = () => {
+ return useQueryState("number", parseAsNumber.withDefault(0));
+};
+```
+
+This is a big improvement! This should mostly solve duplicate key errors or accidentally redefined instances of `useQueryState`. This will encourage two main things:
+
+- definition of `useQueryState`'s distinct from individual components making them easier to share
+- when defined in an external file, it will be easier to identify duplicates as well
+
+## When This Goes Wrong
+
+Most sites I've worked on would organize search params on a per path basis. So when we start to export `useNumberState` and others that share similar names globally, we can run into issues.
+
+```tsx
+import { useQueryState, parseAsString } from "nuqs";
+
+// /home/query-params.ts
+/** Team as abbreviation stored in url */
+export const useTeamFilterState = () => {
+ return useQueryState("team", parseAsString); // expected: team=BOS
+};
+
+// /game/query-params.ts
+/** Team id stored in url */
+export const useTeamFilterState = () => {
+ return useQueryState("team", parseAsString);
+};
+```
+
+On our sports apps, it would be very possible for two pages to have team filters; one with team abbreviation and another with team id. Now, you could solve this by renaming to `useTeamIdFilterState` and so on, but they're not even on the same page! Naming is hard, so let's use the path organization of state to make our lives easier.
+
+## Route Organized useQueryState's
+
+Finally, we land on `(app|pages)/my-page/query-params.ts`. Being mindful of the exported names will simplify our auto-imports, reduce cognitive load on naming, and reduce the surface area for potential misuse.
+
+```ts
+const useGameStartTimeState = () => {
+ return useQueryState("time", parseAsIsoDateTime);
+};
+
+const useGameEndTimeState = () => {
+ return useQueryState("time", parseAsIsoDate);
+};
+
+export const myPageQueryParams = {
+ useGameStartTimeState,
+ useGameEndTimeState,
+};
+```
+
+When each page has a single object of query state's to import it becomes trivial. Here's the final snippet to show how we'd easily hook into our state anywhere within the page now:
+
+```tsx
+import { myPageQueryParams } from "./query-params";
+
+const MyPage = () => {
+ const [gameStartTime] = myPageQueryParams.useGameStartTimeState();
+ const [gameEndTime] = myPageQueryParams.useGameEndTimeState();
+
+ return (
+
+
My Page
+
The game start time is {gameStartTime}
+
The game end time is {gameEndTime}
+
+ );
+};
+```
+
+## A Note on tanstack/router
+
+[Tanstack router](https://tanstack.com/router/latest) has popularized and showed the power of router level typesafe state management of the url. From what I can tell, tanstack router is currently the best way to organize query param state for large apps. That being said, we can replicate similar functionality in nextjs with nuqs + this little exporting pattern.
+
+## Conclusion
+
+The URL is a great place to store state. Nextjs surfaces the search params from the router, but leaves everything else up to the developer. For me, handling the url directly in components as a string or even as URLSearchParams is reminiscent of fetching data from a `useEffect`; you _can_ do it, but you probably shouldn't. So in the same way I would say "you should probably just use tanstack/react-query", I'll say "you should probably use nuqs (and organize your exports)".
diff --git a/src/styles.css b/src/styles.css
index 75be45a..d8db979 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -43,6 +43,24 @@ a:hover {
color: var(--accent-dark);
}
+.prose h1 a,
+.prose h2 a,
+.prose h3 a,
+.prose h4 a,
+.prose h5 a,
+.prose h6 a {
+ color: var(--text-primary-light);
+}
+
+.prose h1 a:hover,
+.prose h2 a:hover,
+.prose h3 a:hover,
+.prose h4 a:hover,
+.prose h5 a:hover,
+.prose h6 a:hover {
+ color: var(--text-secondary-light);
+}
+
.prose {
color: var(--text-primary-light);
}