a:2:{i:0;a:1:{s:4:"data";a:1:{s:7:"entries";a:1:{i:0;a:7:{s:2:"id";s:4:"2477";s:5:"title";s:27:"Using localStorage in Remix";s:11:"displayDate";s:10:"01.19.2023";s:13:"attributeDate";s:10:"2023-01-19";s:4:"body";s:1593:"

I wanted to get some more experience working with GraphQL mutations, so I worked on a new feature for my site that enabled a user to “like” a blog article. I ran into a little gotcha that is kind of obvious now that I know the solution but wasn’t obvious at the time.

I didn’t think I needed to be fancy and store the “like” in the database and associate with the user, so I decided that tracking whether the user had liked the article in localStorage was enough and then keep track of the total article like count in the database. Seems easy enough right?

The Problem

This was my first approach in retrieving whether or not the user had liked the article from localStorage:

And this is the error that I was getting:

ReferenceError: window is not defined

The Fix

At first I was 🤔, but the fix is super simple and totally makes sense. Remix renders on the server, but I wanted to run this in the browser, so I simply needed to wrap it in a useEffect:

This is actually captured in the Remix docs, but it was kind of buried.

";s:13:"numberOfLikes";i:22;s:10:"bodyBlocks";a:7:{i:0;a:3:{s:2:"id";s:4:"2640";s:10:"typeHandle";s:4:"text";s:4:"text";s:884:"

I wanted to get some more experience working with GraphQL mutations, so I worked on a new feature for my site that enabled a user to “like” a blog article. I ran into a little gotcha that is kind of obvious now that I know the solution but wasn’t obvious at the time.

I didn’t think I needed to be fancy and store the “like” in the database and associate with the user, so I decided that tracking whether the user had liked the article in localStorage was enough and then keep track of the total article like count in the database. Seems easy enough right?

The Problem

This was my first approach in retrieving whether or not the user had liked the article from localStorage:

";}i:1;a:4:{s:2:"id";s:4:"2641";s:10:"typeHandle";s:4:"code";s:4:"code";s:199:"import { useState } from 'react' const Like = ({ storageKey, likes }) => { const [hasLiked, setHasLiked] = useState(false) setHasLiked(window.localStorage.getItem(storageKey) === 'true') })";s:4:"lang";s:3:"jsx";}i:2;a:3:{s:2:"id";s:4:"2642";s:10:"typeHandle";s:4:"text";s:4:"text";s:48:"

And this is the error that I was getting:

";}i:3;a:4:{s:2:"id";s:4:"2643";s:10:"typeHandle";s:4:"code";s:4:"code";s:37:"ReferenceError: window is not defined";s:4:"lang";s:4:"text";}i:4;a:3:{s:2:"id";s:4:"2644";s:10:"typeHandle";s:4:"text";s:4:"text";s:232:"

The Fix

At first I was 🤔, but the fix is super simple and totally makes sense. Remix renders on the server, but I wanted to run this in the browser, so I simply needed to wrap it in a useEffect:

";}i:5;a:4:{s:2:"id";s:4:"2645";s:10:"typeHandle";s:4:"code";s:4:"code";s:257:"import { useEffect, useState } from 'react' const Like = ({ storageKey, likes }) => { const [hasLiked, setHasLiked] = useState(false) useEffect(() => { setHasLiked(window.localStorage.getItem(storageKey) === 'true') }, [storageKey]) })";s:4:"lang";s:3:"jsx";}i:6;a:3:{s:2:"id";s:4:"2646";s:10:"typeHandle";s:4:"text";s:4:"text";s:173:"

This is actually captured in the Remix docs, but it was kind of buried.

";}}}}}}i:1;O:25:"yii\caching\TagDependency":3:{s:4:"tags";a:14:{i:0;s:7:"element";i:1;s:29:"element::craft\elements\Entry";i:2;s:40:"element::craft\elements\Entry::section:4";i:3;s:35:"element::craft\elements\MatrixBlock";i:4;s:41:"element::craft\elements\MatrixBlock::2640";i:5;s:41:"element::craft\elements\MatrixBlock::2641";i:6;s:41:"element::craft\elements\MatrixBlock::2642";i:7;s:41:"element::craft\elements\MatrixBlock::2643";i:8;s:41:"element::craft\elements\MatrixBlock::2644";i:9;s:41:"element::craft\elements\MatrixBlock::2645";i:10;s:41:"element::craft\elements\MatrixBlock::2646";i:11;s:29:"element::craft\elements\Asset";i:12;s:35:"element::craft\elements\Asset::2490";i:13;s:7:"graphql";}s:4:"data";a:14:{s:40:"CraftCMSce35088bdfe0816226cd17fd051a4803";s:21:"0.46162700 1707493692";s:40:"CraftCMS2743a789e8993267a348eee1ee7e4450";s:21:"0.34758900 1706282441";s:40:"CraftCMS2ac34726f299f7e18e449d8e536c67f8";s:21:"0.22771600 1711953912";s:40:"CraftCMSd9961e9460af421f810dce3dc85f38f9";s:21:"0.96283300 1706283345";s:40:"CraftCMS36408baf6977a45751e0965cdfd7eb13";s:21:"0.43664400 1706308690";s:40:"CraftCMS937f7c59ce6a64c744f12965261771e1";s:21:"0.43664400 1706308690";s:40:"CraftCMS2e5da15f9409549825a41b97199fccfa";s:21:"0.43664400 1706308690";s:40:"CraftCMSa90a259b1bc12c4fe8836d475f6f3e28";s:21:"0.43664400 1706308690";s:40:"CraftCMS00cd6e4e7f131f004dcbad857c274ec8";s:21:"0.43664400 1706308690";s:40:"CraftCMS649adc96e40b8c3cd59cdfcd60a9b54e";s:21:"0.43664400 1706308690";s:40:"CraftCMSea9f2b86f58ee2571e4d12df51a685cc";s:21:"0.43664400 1706308690";s:40:"CraftCMS656ba125f2735c9321627f00daa5a4cd";s:21:"0.21062500 1707493467";s:40:"CraftCMS4b1311fa1eded8bb4c0a2c260b2357ca";s:21:"0.71764500 1707532418";s:40:"CraftCMS3817d4a648fcfac939af46605323feb0";s:21:"0.87777500 1713035607";}s:8:"reusable";b:0;}}