1<!DOCTYPE html><html lang="en"><head><title>Rebuilding Mercari's Search: Home and Menu</title><meta charSet="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5"/><link rel="canonical" href="https://www.iamtk.co/series/rebuilding-mercaris-search/home-menu"/><link rel="icon" href="/favicon.ico"/><meta name="robots" content="index,follow"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="HandheldFriendly" content="True"/><meta name="description" content="Building the Home page with NextJS, TypeScript, Prettier, and Jest to start rebuilding the Mercari's Search."/><meta name="keywords" content="javascript,typescript,web_development,devtools,software_engineering,reactjs,nextjs,frontend,backend"/><meta property="og:type" content="website"/><meta property="og:title" content="Rebuilding Mercari's Search: Home and Menu"/><meta property="og:description" content="Building the Home page with NextJS, TypeScript, Prettier, and Jest to start rebuilding the Mercari's Search."/><meta property="og:locale" content="en"/><meta property="og:image" content="https://iamtk.co/series/rebuilding-mercaris-search/house.jpg"/><meta property="og:updated_time" content="2020-06-21T00:00:00.000Z"/><meta name="twitter:card" content="summary"/><meta name="twitter:title" content="Rebuilding Mercari's Search: Home and Menu"/><meta name="twitter:description" content="Building the Home page with NextJS, TypeScript, Prettier, and Jest to start rebuilding the Mercari's Search."/><meta name="twitter:image" content="https://iamtk.co/series/rebuilding-mercaris-search/house.jpg"/><meta name="next-head-count" content="20"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/3be0d5954ed12807.css" as="style"/><link rel="stylesheet" href="/_next/static/css/3be0d5954ed12807.css" data-n-g=""/><noscript data-n-css=""></noscript>
1<script defer="" nomodule="" src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script>
1<script src="/_next/static/chunks/webpack-6b7a0d9b5f9252f5.js" defer=""></script>
1<script src="/_next/static/chunks/framework-fa214812e1a9ea63.js" defer=""></script>
1<script src="/_next/static/chunks/main-772348ed454bbb86.js" defer=""></script>
1<script src="/_next/static/chunks/pages/_app-aa2321cbe9aae3d6.js" defer=""></script>
1<script src="/_next/static/chunks/a9a7754c-6e6f019b517c51df.js" defer=""></script>
1<script src="/_next/static/chunks/b5f2ed29-c2658fc8d851263f.js" defer=""></script>
1<script src="/_next/static/chunks/348-e6f346d4ca62119b.js" defer=""></script>
1<script src="/_next/static/chunks/849-0809741e14136b17.js" defer=""></script>
1<script src="/_next/static/chunks/845-5f1ade2dd06e7e5c.js" defer=""></script>
1<script src="/_next/static/chunks/pages/series/%5Bseries%5D/%5BseriesItem%5D-ef36ebf3dcde76a7.js" defer=""></script>
1<script src="/_next/static/v8jrEN7x-HFMMDD4KoSpB/_buildManifest.js" defer=""></script>
1<script src="/_next/static/v8jrEN7x-HFMMDD4KoSpB/_ssgManifest.js" defer=""></script>
1</head><body><div id="__next">
1<script>((e,s,n,l,o,d,u,h)=>{let m=document.documentElement,w=["light","dark"];function p(r){(Array.isArray(e)?e:[e]).forEach(y=>{let k=y==="class",S=k&&d?o.map(f=>d[f]||f):o;k?(m.classList.remove(...S),m.classList.add(d&&d[r]?d[r]:r)):m.setAttribute(y,r)}),C(r)}function C(r){h&&w.includes(r)&&(m.style.colorScheme=r)}function a(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}if(l)p(l);else try{let r=localStorage.getItem(s)||n,y=u&&r==="system"?a():r;p(y)}catch(r){}})("data-theme","theme","system",null,["light","dark"],null,true,true)</script>
1<div class="css-16aoy09" style="opacity:0"><style data-emotion="css 70fgq2">.css-70fgq2{padding:24px 16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;}</style><header class="css-70fgq2"><style data-emotion="css 1qdjrf6">.css-1qdjrf6{font-weight:700;font-size:16px;-webkit-text-decoration:none;-webkit-text-decoration:none;text-decoration:none;border:1px solid;padding:8px 10px;}</style><a class="css-1qdjrf6" href="/">TK</a><style data-emotion="css iwiovi">.css-iwiovi{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:8px;}</style><div class="css-iwiovi"><style data-emotion="css 1fu54f8">.css-1fu54f8{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;width:36px;height:36px;border:1px solid;border-radius:50%;background:none;color:inherit;cursor:pointer;padding:0;}html[data-theme='light'] .css-1fu54f8{color:#333;}</style><button type="button" aria-label="Open display settings" class="css-1fu54f8"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="sliders-h" class="svg-inline--fa fa-sliders-h fa-w-16 fa-sm " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M496 384H160v-16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v16H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h80v16c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-16h336c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-160h-80v-16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v16H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h336v16c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-16h80c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-160H288V48c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v16H16C7.2 64 0 71.2 0 80v32c0 8.8 7.2 16 16 16h208v16c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-16h208c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16z"></path></svg></button></div></header><div class="css-16aoy09" style="opacity:0"><div class="content" style="font-size:15px;--content-max-width:680px"><a id="menu-icon" data-testid="home-link" class="css-1qqpmv" href="/"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="long-arrow-alt-left" class="svg-inline--fa fa-long-arrow-alt-left fa-w-14 css-1fc8vz0" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M134.059 296H436c6.627 0 12-5.373 12-12v-56c0-6.627-5.373-12-12-12H134.059v-46.059c0-21.382-25.851-32.09-40.971-16.971L7.029 239.029c-9.373 9.373-9.373 24.569 0 33.941l86.059 86.059c15.119 15.119 40.971 4.411 40.971-16.971V296z"></path></svg>Home</a><article class="post" itemscope="" itemType="http://schema.org/BlogPosting"><header><h1 class="css-1vjntb0"><span data-br=":Rjalml76:" data-brr="1" style="display:inline-block;vertical-align:top;text-decoration:inherit;text-wrap:balance">Rebuilding Mercari's Search: Home and Menu</span>
1<script>self.__wrap_n=self.__wrap_n||(self.CSS&&CSS.supports("text-wrap","balance")?1:2);self.__wrap_b=(e,n,t)=>{t=t||document.querySelector(`[data-br="${e}"]`);let r=t==null?void 0:t.parentElement;if(!r)return;let o=u=>t.style.maxWidth=u+"px";t.style.maxWidth="";let l=r.clientWidth,d=r.clientHeight,s=l/2-.25,a=l+.5,c;if(l){for(o(s),s=Math.max(t.scrollWidth,s);s+1<a;)c=Math.round((s+a)/2),o(c),r.clientHeight===d?a=c:s=c;o(a*n+l*(1-n))}t.__wrap_o||(typeof ResizeObserver!="undefined"?(t.__wrap_o=new ResizeObserver(()=>{self.__wrap_b(0,+t.dataset.brr,t)})).observe(r):process.env.NODE_ENV==="development"&&console.warn("The browser you are using does not support the ResizeObserver API. Please consider add polyfill for this API to avoid potential layout shifts or upgrade your browser. Read more: https://github.com/shuding/react-wrap-balancer#browser-support-information"))};self.__wrap_n!=1&&self.__wrap_b(":Rjalml76:",1)</script>
1</h1><div style="margin-top:6px"><span class="author" itemProp="author" itemscope="" itemType="http://schema.org/Person"><span itemProp="name" style="font-size:0.85rem">TK</span></span><div style="display:inline;margin-left:8px;margin-right:8px;font-size:0.85rem"><time dateTime="2021-06-20T00:00:00.000Z" itemProp="datePublished">2021-06-20</time></div><span class="css-1nh9f23">⢠<!-- -->4<!-- --> min read</span><div><div style="font-size:0.85rem;display:inline"><a style="margin-right:8px;display:inline-block" href="/tags/web-development">#web_development</a><a style="margin-right:8px;display:inline-block" href="/tags/typescript">#typescript</a><a style="margin-right:8px;display:inline-block" href="/tags/javascript">#javascript</a></div></div></div></header><style data-emotion="css p4z7j6">.css-p4z7j6{margin-top:2em;margin-bottom:2em;text-align:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:12px;}</style><div class="css-p4z7j6"><img alt="house" loading="lazy" width="592" height="394" decoding="async" data-nimg="1" style="color:transparent;background-size:cover;background-position:50% 50%;background-repeat:no-repeat;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 592 394'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'/%3E%3C/feComponentTransfer%3E%%3C/filter%3E%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAADAAQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAfEAAABgIDAQAAAAAAAAAAAAAAAQIDBAUREgcIIVH/xAAVAQEBAAAAAAAAAAAAAAAAAAADBP/EABoRAAICAwAAAAAAAAAAAAAAAAACARESITH/2gAMAwEAAhEDEQA/AJ52Etp9ZyvcMwZbrLStHDQlXmxpLOPgAANXbGNlUot8P//Z'/%3E%3C/svg%3E")" srcSet="/_next/image?url=%2Fseries%2Frebuilding-mercaris-search%2Fhouse.jpg&w=640&q=75 1x, /_next/image?url=%2Fseries%2Frebuilding-mercaris-search%2Fhouse.jpg&w=1200&q=75 2x" src="/_next/image?url=%2Fseries%2Frebuilding-mercaris-search%2Fhouse.jpg&w=1200&q=75"/><span style="font-size:12px">Photo by<!-- --> <a href="https://unsplash.com/@insolitus" style="text-decoration:none" target="_blank" rel="noreferrer">Rowan Heuvel</a></span></div><p style="opacity:0">This is the second post of the <a href="/series/rebuilding-mercaris-search">Rebuilding Mercari's Search series</a>.</p><p style="opacity:0">In this post, we'll talk about the home and the menu drafts. Before starting to implement the search, the filter, and the products list, I wanted to add a very simple home and a very simple menu to do the navigation between the home (url: <code>/</code>) and the search (url: <code>/search</code>) pages.</p><p style="opacity:0">As we start to implement UI, I should tell you that my focus on this project was to do the logic, state management, and tests. This is why I was looking for a UI framework to work as my design system. With this idea, I don't need to work heavily with CSS.</p><p style="opacity:0">I chose Material UI, because I'm familiar with it, so I can focus on other parts instead of learning a new framework now.</p><p style="opacity:0">So let's install the library and start implement the home and the menu.</p><pre style="opacity:0"><code class="hljs language-bash">yarn add @material-ui/core 2</code></pre><p style="opacity:0">The home is just a page with a title for now as I want to focus on the navigation between the home and the search.</p><p style="opacity:0">So the home, that will live in the <code>/</code> url, will have the menu to navigate to the search page, which will live in the <code>/search</code> url for now.</p><p style="opacity:0">This is the home with the title:</p><pre style="opacity:0"><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Home</span>(<span class="hljs-params"></span>) { 3 <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">h1</span>></span>Home<span class="hljs-tag"></<span class="hljs-name">h1</span>></span></span>; 4} 5</code></pre><p style="opacity:0">This component page lives in the <code>pages/index.tsx</code> as NextJS does routing based on the folder and file structures.</p><p style="opacity:0">To make all the pages have a menu to be able to navigate to other places, we need to add it to the <code>pages/_app.tsx</code> file. But to be able to do that, let's first implement our Menu component.</p><p style="opacity:0">To simplify the menu at first, I wanted to add a button that will open or close the menu showing or hiding the menu items.</p><pre style="opacity:0"><code class="hljs language-tsx"><<span class="hljs-title class_">Button</span> aria-controls=<span class="hljs-string">"menu"</span> aria-haspopup=<span class="hljs-string">"true"</span> onClick={handleClick}> 6 <span class="hljs-title class_">Open</span> <span class="hljs-title class_">Menu</span> 7</<span class="hljs-title class_">Button</span>> 8</code></pre><p style="opacity:0">This is the first part. Nothing much here. We need the <code>handleClick</code> function now.</p><pre style="opacity:0"><code class="hljs language-tsx"><span class="hljs-keyword">const</span> [anchorEl, setAnchorEl] = useState<<span class="hljs-literal">null</span> | <span class="hljs-title class_">HTMLElement</span>>(<span class="hljs-literal">null</span>); 9 10<span class="hljs-keyword">const</span> <span class="hljs-title function_">handleClick</span> = (<span class="hljs-params"><span class="hljs-attr">event</span>: <span class="hljs-title class_">MouseEvent</span><<span class="hljs-title class_">HTMLButtonElement</span>></span>) => { 11 <span class="hljs-title function_">setAnchorEl</span>(event.<span class="hljs-property">currentTarget</span>); 12}; 13</code></pre><p style="opacity:0">The <code>handleClick</code> function only manage the state related to the <code>anchorEl</code>.</p><p style="opacity:0">The rest of the menu UI is pretty simple, we only need to use the <code>Menu</code> and the <code>MenuItem</code> from the Material UI.</p><pre style="opacity:0"><code class="hljs language-tsx"><<span class="hljs-title class_">MuiMenu</span> 14 id=<span class="hljs-string">"menu"</span> 15 anchorEl={anchorEl} 16 keepMounted 17 open={<span class="hljs-title class_">Boolean</span>(anchorEl)} 18 onClose={handleClose} 19> 20 {menuItems.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">item</span>: <span class="hljs-title class_">MenuItem</span></span>) =></span> ( 21 <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">{item.linkTo}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.key}</span>></span>
22 <span class="hljs-tag"><<span class="hljs-name">MuiMenuItem</span>></span>{item.label}<span class="hljs-tag"></<span class="hljs-name">MuiMenuItem</span>></span> 23 <span class="hljs-tag"></<span class="hljs-name">Link</span>></span></span> 24 ))} 25</<span class="hljs-title class_">MuiMenu</span>> 26</code></pre><p style="opacity:0">We know about the <code>anchorEl</code>, but we need to implement the <code>handleClose</code> function, the <code>menuItems</code> list, and the <code>MenuItem</code> type.</p><p style="opacity:0">The <code>MenuItem</code> is a "type" representation for each item:</p><pre style="opacity:0"><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">MenuItem</span> = { 27 <span class="hljs-attr">linkTo</span>: <span class="hljs-built_in">string</span>; 28 <span class="hljs-attr">label</span>: <span class="hljs-built_in">string</span>; 29 <span class="hljs-attr">key</span>: <span class="hljs-built_in">string</span>; 30}; 31</code></pre><p style="opacity:0">The <code>menuItems</code> is the simplest as it is just a list of items passed as a prop to this menu component. But we need to make sure it is passed and the "type" representation of this list is in the function component contract.</p><pre style="opacity:0"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">FunctionComponent</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; 32 33<span class="hljs-keyword">type</span> <span class="hljs-title class_">MenuPropsType</span> = { <span class="hljs-attr">menuItems</span>: <span class="hljs-title class_">MenuItem</span>[] }; 34 35<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title class_">Menu</span>: <span class="hljs-title class_">FunctionComponent</span><<span class="hljs-title class_">MenuPropsType</span>> = <span class="hljs-function">(<span class="hljs-params">{ menuItems }</span>) =></span> { 36 ... 37}; 38</code></pre><p style="opacity:0">And the <code>handleClose</code> function only undo what the <code>handleClick</code> does: reset the <code>anchorEl</code> with a <code>null</code> value.</p><pre style="opacity:0"><code class="hljs language-tsx"><span class="hljs-keyword">const</span> <span class="hljs-title function_">handleClose</span> = (<span class="hljs-params"><span class="hljs-attr">_</span>: <span class="hljs-built_in">any</span></span>) => { 39 <span class="hljs-title function_">setAnchorEl</span>(<span class="hljs-literal">null</span>); 40}; 41</code></pre><p style="opacity:0">Nice! The menu is not complete nor pretty, but it's functional now. We can start using in our app. To be able to reuse for all pages (for this app, it'll be for the home and the search pages), we just need to add it to the <code>_app.tsx</code> file.</p><pre style="opacity:0"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Fragment</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; 42<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">AppProps</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/app'</span>; 43<span class="hljs-keyword">import</span> <span class="hljs-title class_">Menu</span>, { <span class="hljs-title class_">MenuItem</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'Base/Layout/Menu'</span>; 44<span class="hljs-keyword">import</span> <span class="hljs-string">'../styles/globals.css'</span>; 45 46<span class="hljs-keyword">const</span> <span class="hljs-attr">menuItems</span>: <span class="hljs-title class_">MenuItem</span>[] = [ 47 { 48 <span class="hljs-attr">linkTo</span>: <span class="hljs-string">'/'</span>, 49 <span class="hljs-attr">label</span>: <span class="hljs-string">'Home'</span>, 50 <span class="hljs-attr">key</span>: <span class="hljs-string">'link-to-home'</span>, 51 }, 52 { 53 <span class="hljs-attr">linkTo</span>: <span class="hljs-string">'/search'</span>, 54 <span class="hljs-attr">label</span>: <span class="hljs-string">'Search'</span>, 55 <span class="hljs-attr">key</span>: <span class="hljs-string">'link-to-search'</span>, 56 }, 57]; 58 59<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params">{ Component, pageProps }: <span class="hljs-title class_">AppProps</span></span>) { 60 <span class="hljs-keyword">return</span> ( 61 <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Fragment</span>></span>
62 <span class="hljs-tag"><<span class="hljs-name">Menu</span> <span class="hljs-attr">menuItems</span>=<span class="hljs-string">{menuItems}</span> /></span> 63 <span class="hljs-tag"><<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /></span> 64 <span class="hljs-tag"></<span class="hljs-name">Fragment</span>></span></span> 65 ); 66} 67 68<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">App</span>; 69</code></pre><p style="opacity:0">The <code>Component</code> is each page we want to create and we add the <code>Menu</code> at the top of the page. As we saw earlier, we need to pass the <code>menuItems</code> with the type contract we built in the menu. It's also nice that we can reuse the types to make the data consistent.</p><h2 id="final-words" style="opacity:0">Final words</h2><p style="opacity:0">This was the second post of the <em>Rebuilding Mercari's Search</em> series and I hope this also provides value to you. In this post, we start creating the building blocks for the app by adding a very simple home and menu for navigation.</p><p style="opacity:0">For the following post, we'll talk about the implementation of the search page, where we'll focus on the products list UI first.</p><h2 id="resources" style="opacity:0">Resources</h2><ul style="opacity:0"> 70<li style="opacity:0"><a href="https://github.com/imteekay/mercari-search/pull/1">Home & Menu Pull Request</a></li> 71<li style="opacity:0"><a href="https://github.com/imteekay/mercari-search">mercari-search repo</a></li> 72</ul><style data-emotion="css 1i2p5ha">.css-1i2p5ha{position:fixed;bottom:120px;right:130px;width:300px;font-size:12px;line-height:1.5em;background-color:#40a578;color:white;box-shadow:0 10px 15px -3px rgb(0 0 0 / 0.1),0 4px 6px -4px rgb(0 0 0 / 0.1);padding:10px;border-radius:6px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:translateX(500px);-moz-transform:translateX(500px);-ms-transform:translateX(500px);transform:translateX(500px);-webkit-transition:-webkit-transform 1.2s;transition:transform 1.2s;transition-timing-function:linear( 73 0, 74 0.035 2.1%, 75 0.281 6.7%, 76 0.723 12.9%, 77 0.938 16.7%, 78 0.977 51%, 79 0.974 53.8%, 80 0.975 57.1%, 81 0.997 69.8%, 82 1 83 );}.css-1i2p5ha.animated{-webkit-transform:translateX(0) rotate(-5deg);-moz-transform:translateX(0) rotate(-5deg);-ms-transform:translateX(0) rotate(-5deg);transform:translateX(0) rotate(-5deg);transition-timing-function:linear( 84 0, 85 0.009, 86 0.035 2.1%, 87 0.141, 88 0.281 6.7%, 89 0.723 12.9%, 90 0.938 16.7%, 91 1.017, 92 1.077, 93 1.121, 94 1.149 24.3%, 95 1.159, 96 1.163, 97 1.161, 98 1.154 29.9%, 99 1.129 32.8%, 100 1.051 39.6%, 101 1.017 43.1%, 102 0.991, 103 0.977 51%, 104 0.974 53.8%, 105 0.975 57.1%, 106 0.997 69.8%, 107 1.003 76.9%, 108 1.004 83.8%, 109 1 110 );}@media (max-width: 600px){.css-1i2p5ha{display:none;}}</style><div id="test" class=" css-1i2p5ha"><style data-emotion="css 1sg2lsz">.css-1sg2lsz{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}</style><div class="css-1sg2lsz"><style data-emotion="css 1ugv5lf">.css-1ugv5lf{padding-right:8px;}</style><div class="css-1ugv5lf">Hey! You may like this<!-- --> <a target="_blank" rel="noreferrer" href="https://teekay.substack.com"><b>newsletter</b></a> <!-- -->if you're enjoying this blog. â¤</div><style data-emotion="css 1mfr5wz">.css-1mfr5wz{padding:6px;position:absolute;top:0;right:0;cursor:pointer;}</style><a role="button" class="css-1mfr5wz">â</a></div></div><hr/><p><a class="icon" target="_blank" href="https://twitter.com/wordsofteekay" title="twitter" rel="noreferrer"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="svg-inline--fa fa-twitter fa-w-16 " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
110<path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg> Twitter</a> · <a class="icon" target="_blank" href="https://github.com/imteekay" title="github" rel="noreferrer"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github fa-w-16 " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> Github</a></p></article><iframe src="https://teekay.substack.com/embed" width="100%" height="300" style="margin-top:30px;margin-bottom:30px;border:1px solid #eee;background:white" title="TK's Substack"></iframe><div class="css-1wk4ut1"><div style="font-size:0.85rem;display:inline"><a style="margin-right:8px;display:inline-block" href="/tags/web-development">#web_development</a><a style="margin-right:8px;display:inline-block" href="/tags/typescript">#typescript</a><a style="margin-right:8px;display:inline-block" href="/tags/javascript">#javascript</a></div></div></div></div><footer id="footer" class="footer"><div class="footer-left">Copyright © <!-- -->2026<!-- --> TK</div><div class="footer-right"><nav><ul><li><a href="/">Home</a></li><li><a href="/writings">Writing</a></li><li><a href="/support">Support</a></li><li><a href="https://github.com/imteekay/iamtk.co" target="_blank" rel="noopener noreferrer">Code</a></li><li><a href="/rss.xml" target="_blank" rel="noopener noreferrer">RSS</a></li></ul></nav></div></footer></div></div>
110<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"content":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n a: \"a\",\n code: \"code\",\n em: \"em\",\n h2: \"h2\",\n li: \"li\",\n p: \"p\",\n pre: \"pre\",\n span: \"span\",\n ul: \"ul\",\n ..._provideComponents(),\n ...props.components\n }, {SmoothRender} = _components;\n if (!SmoothRender) _missingMdxReference(\"SmoothRender\", true);\n return _jsxs(SmoothRender, {\n children: [_jsxs(_components.p, {\n children: [\"This is the second post of the \", _jsx(_components.a, {\n href: \"/series/rebuilding-mercaris-search\",\n children: \"Rebuilding Mercari's Search series\"\n }), \".\"]\n }), _jsxs(_components.p, {\n children: [\"In this post, we'll talk about the home and the menu drafts. Before starting to implement the search, the filter, and the products list, I wanted to add a very simple home and a very simple menu to do the navigation between the home (url: \", _jsx(_components.code, {\n children: \"/\"\n }), \") and the search (url: \", _jsx(_components.code, {\n children: \"/search\"\n }), \") pages.\"]\n }), _jsx(_components.p, {\n children: \"As we start to implement UI, I should tell you that my focus on this project was to do the logic, state management, and tests. This is why I was looking for a UI framework to work as my design system. With this idea, I don't need to work heavily with CSS.\"\n }), _jsx(_components.p, {\n children: \"I chose Material UI, because I'm familiar with it, so I can focus on other parts instead of learning a new framework now.\"\n }), _jsx(_components.p, {\n children: \"So let's install the library and start implement the home and the menu.\"\n }), _jsx(_components.pre, {\n children: _jsx(_components.code, {\n className: \"hljs language-bash\",\n children: \"yarn add @material-ui/core\\n\"\n })\n }), _jsx(_components.p, {\n children: \"The home is just a page with a title for now as I want to focus on the navigation between the home and the search.\"\n }), _jsxs(_components.p, {\n children: [\"So the home, that will live in the \", _jsx(_components.code, {\n children: \"/\"\n }), \" url, will have the menu to navigate to the search page, which will live in the \", _jsx(_components.code, {\n children: \"/search\"\n }), \" url for now.\"]\n }), _jsx(_components.p, {\n children: \"This is the home with the title:\"\n }), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [_jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"export\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"default\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"function\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"Home\"\n }), \"(\", _jsx(_components.span, {\n className: \"hljs-params\"\n }), \") {\\n \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"return\"\n }), \" \", _jsxs(_components.span, {\n className: \"xml\",\n children: [_jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"h1\"\n }), \"\u003e\"]\n }), \"Home\", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c/\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"h1\"\n }), \"\u003e\"]\n })]\n }), \";\\n}\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"This component page lives in the \", _jsx(_components.c
110ode, {\n children: \"pages/index.tsx\"\n }), \" as NextJS does routing based on the folder and file structures.\"]\n }), _jsxs(_components.p, {\n children: [\"To make all the pages have a menu to be able to navigate to other places, we need to add it to the \", _jsx(_components.code, {\n children: \"pages/_app.tsx\"\n }), \" file. But to be able to do that, let's first implement our Menu component.\"]\n }), _jsx(_components.p, {\n children: \"To simplify the menu at first, I wanted to add a button that will open or close the menu showing or hiding the menu items.\"\n }), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Button\"\n }), \" aria-controls=\", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"\\\"menu\\\"\"\n }), \" aria-haspopup=\", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"\\\"true\\\"\"\n }), \" onClick={handleClick}\u003e\\n \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Open\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Menu\"\n }), \"\\n\u003c/\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Button\"\n }), \"\u003e\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"This is the first part. Nothing much here. We need the \", _jsx(_components.code, {\n children: \"handleClick\"\n }), \" function now.\"]\n }), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [_jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"const\"\n }), \" [anchorEl, setAnchorEl] = useState\u003c\", _jsx(_components.span, {\n className: \"hljs-literal\",\n children: \"null\"\n }), \" | \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"HTMLElement\"\n }), \"\u003e(\", _jsx(_components.span, {\n className: \"hljs-literal\",\n children: \"null\"\n }), \");\\n\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"const\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"handleClick\"\n }), \" = (\", _jsxs(_components.span, {\n className: \"hljs-params\",\n children: [_jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"event\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MouseEvent\"\n }), \"\u003c\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"HTMLButtonElement\"\n }), \"\u003e\"]\n }), \") =\u003e {\\n \", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"setAnchorEl\"\n }), \"(event.\", _jsx(_components.span, {\n className: \"hljs-property\",\n children: \"currentTarget\"\n }), \");\\n};\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"The \", _jsx(_components.code, {\n children: \"handleClick\"\n }), \" function only manage the state related to the \", _jsx(_components.code, {\n children: \"anchorEl\"\n }), \".\"]\n }), _jsxs(_components.p, {\n children: [\"The rest of the menu UI is pretty simple, we only need to use the \", _jsx(_components.code, {\n children: \"Menu\"\n }), \" and the \", _jsx(_components.code, {\n children: \"MenuItem\"\n }), \" from the Material UI.\"]\n }
110), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MuiMenu\"\n }), \"\\n id=\", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"\\\"menu\\\"\"\n }), \"\\n anchorEl={anchorEl}\\n keepMounted\\n open={\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Boolean\"\n }), \"(anchorEl)}\\n onClose={handleClose}\\n\u003e\\n {menuItems.\", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"map\"\n }), \"(\", _jsxs(_components.span, {\n className: \"hljs-function\",\n children: [\"(\", _jsxs(_components.span, {\n className: \"hljs-params\",\n children: [_jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"item\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuItem\"\n })]\n }), \") =\u003e\"]\n }), \" (\\n \", _jsxs(_components.span, {\n className: \"xml\",\n children: [_jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"Link\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"href\"\n }), \"=\", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"{item.linkTo}\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"key\"\n }), \"=\", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"{item.key}\"\n }), \"\u003e\"]\n }), \"\\n \", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"MuiMenuItem\"\n }), \"\u003e\"]\n }), \"{item.label}\", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c/\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"MuiMenuItem\"\n }), \"\u003e\"]\n }), \"\\n \", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c/\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"Link\"\n }), \"\u003e\"]\n })]\n }), \"\\n ))}\\n\u003c/\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MuiMenu\"\n }), \"\u003e\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"We know about the \", _jsx(_components.code, {\n children: \"anchorEl\"\n }), \", but we need to implement the \", _jsx(_components.code, {\n children: \"handleClose\"\n }), \" function, the \", _jsx(_components.code, {\n children: \"menuItems\"\n }), \" list, and the \", _jsx(_components.code, {\n children: \"MenuItem\"\n }), \" type.\"]\n }), _jsxs(_components.p, {\n children: [\"The \", _jsx(_components.code, {\n children: \"MenuItem\"\n }), \" is a \\\"type\\\" representation for each item:\"]\n }), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [_jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"export\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"type\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuItem\"\n }), \" = {\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"linkTo\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-built_in\",\n children: \"string\"\n }), \";\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"label\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-built_in\",\n children: \"string\"\n }), \";\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"key\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-built_in\",\n children: \"string\"\n }), \";\\n};\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"The \", _jsx(_components.c
110ode, {\n children: \"menuItems\"\n }), \" is the simplest as it is just a list of items passed as a prop to this menu component. But we need to make sure it is passed and the \\\"type\\\" representation of this list is in the function component contract.\"]\n }), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [_jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"import\"\n }), \" { \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"FunctionComponent\"\n }), \" } \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"from\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'react'\"\n }), \";\\n\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"type\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuPropsType\"\n }), \" = { \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"menuItems\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuItem\"\n }), \"[] };\\n\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"export\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"const\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Menu\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"FunctionComponent\"\n }), \"\u003c\", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuPropsType\"\n }), \"\u003e = \", _jsxs(_components.span, {\n className: \"hljs-function\",\n children: [\"(\", _jsx(_components.span, {\n className: \"hljs-params\",\n children: \"{ menuItems }\"\n }), \") =\u003e\"]\n }), \" {\\n\\t...\\n};\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"And the \", _jsx(_components.code, {\n children: \"handleClose\"\n }), \" function only undo what the \", _jsx(_components.code, {\n children: \"handleClick\"\n }), \" does: reset the \", _jsx(_components.code, {\n children: \"anchorEl\"\n }), \" with a \", _jsx(_components.code, {\n children: \"null\"\n }), \" value.\"]\n }), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [_jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"const\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"handleClose\"\n }), \" = (\", _jsxs(_components.span, {\n className: \"hljs-params\",\n children: [_jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"_\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-built_in\",\n children: \"any\"\n })]\n }), \") =\u003e {\\n \", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"setAnchorEl\"\n }), \"(\", _jsx(_components.span, {\n className: \"hljs-literal\",\n children: \"null\"\n }), \");\\n};\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"Nice! The menu is not complete nor pretty, but it's functional now. We can start using in our app. To be able to reuse for all pages (for this app, it'll be for the home and the search pages), we just need to add it to the \", _jsx(_components.code, {\n children: \"_app.tsx\"\n }), \" file.\"]\n }
110), _jsx(_components.pre, {\n children: _jsxs(_components.code, {\n className: \"hljs language-tsx\",\n children: [_jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"import\"\n }), \" { \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Fragment\"\n }), \" } \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"from\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'react'\"\n }), \";\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"import\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"type\"\n }), \" { \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"AppProps\"\n }), \" } \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"from\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'next/app'\"\n }), \";\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"import\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"Menu\"\n }), \", { \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuItem\"\n }), \" } \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"from\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'Base/Layout/Menu'\"\n }), \";\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"import\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'../styles/globals.css'\"\n }), \";\\n\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"const\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"menuItems\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"MenuItem\"\n }), \"[] = [\\n {\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"linkTo\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'/'\"\n }), \",\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"label\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'Home'\"\n }), \",\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"key\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'link-to-home'\"\n }), \",\\n },\\n {\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"linkTo\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'/search'\"\n }), \",\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"label\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'Search'\"\n }), \",\\n \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"key\"\n }), \": \", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"'link-to-search'\"\n }), \",\\n },\\n];\\n\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"function\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title function_\",\n children: \"App\"\n }), \"(\", _jsxs(_components.span, {\n className: \"hljs-params\",\n children: [\"{ Component, pageProps }: \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"AppProps\"\n })]\n }), \") {\\n \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"return\"\n }
110), \" (\\n \", _jsxs(_components.span, {\n className: \"xml\",\n children: [_jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"Fragment\"\n }), \"\u003e\"]\n }), \"\\n \", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"Menu\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"menuItems\"\n }), \"=\", _jsx(_components.span, {\n className: \"hljs-string\",\n children: \"{menuItems}\"\n }), \" /\u003e\"]\n }), \"\\n \", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"Component\"\n }), \" {\", _jsx(_components.span, {\n className: \"hljs-attr\",\n children: \"...pageProps\"\n }), \"} /\u003e\"]\n }), \"\\n \", _jsxs(_components.span, {\n className: \"hljs-tag\",\n children: [\"\u003c/\", _jsx(_components.span, {\n className: \"hljs-name\",\n children: \"Fragment\"\n }), \"\u003e\"]\n })]\n }), \"\\n );\\n}\\n\\n\", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"export\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-keyword\",\n children: \"default\"\n }), \" \", _jsx(_components.span, {\n className: \"hljs-title class_\",\n children: \"App\"\n }), \";\\n\"]\n })\n }), _jsxs(_components.p, {\n children: [\"The \", _jsx(_components.code, {\n children: \"Component\"\n }), \" is each page we want to create and we add the \", _jsx(_components.code, {\n children: \"Menu\"\n }), \" at the top of the page. As we saw earlier, we need to pass the \", _jsx(_components.code, {\n children: \"menuItems\"\n }), \" with the type contract we built in the menu. It's also nice that we can reuse the types to make the data consistent.\"]\n }), _jsx(_components.h2, {\n children: \"Final words\"\n }), _jsxs(_components.p, {\n children: [\"This was the second post of the \", _jsx(_components.em, {\n children: \"Rebuilding Mercari's Search\"\n }), \" series and I hope this also provides value to you. In this post, we start creating the building blocks for the app by adding a very simple home and menu for navigation.\"]\n }), _jsx(_components.p, {\n children: \"For the following post, we'll talk about the implementation of the search page, where we'll focus on the products list UI first.\"\n }), _jsx(_components.h2, {\n children: \"Resources\"\n }), _jsxs(_components.ul, {\n children: [\"\\n\", _jsx(_components.li, {\n children: _jsx(_components.a, {\n href: \"https://github.com/imteekay/mercari-search/pull/1\",\n children: \"Home \u0026 Menu Pull Request\"\n })\n }), \"\\n\", _jsx(_components.li, {\n children: _jsx(_components.a, {\n href: \"https://github.com/imteekay/mercari-search\",\n children: \"mercari-search repo\"\n })\n }), \"\\n\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\nfunction _missingMdxReference(id, component) {\n throw new Error(\"Expected \" + (component ? \"component\" : \"object\") + \" `\" + id + \"` to be defined: you likely forgot to import, pass, or provide it.\");\n}\n","frontmatter":{},"scope":{}},"postMetadata":{"title":"Rebuilding Mercari's Search: Home and Menu","description":"Building the Home page with NextJS, TypeScript, Prettier, and Jest to start rebuilding the Mercari's Search.","date":"2021-06-20","tags":[{"href":"/tags/web-development","name":"web_development"},{"href":"/tags/typescript","name":"typescript"},{"href":"/tags/javascript","name":"javascript"}],"coverImage":{"src":"/series/rebuilding-mercaris-search/house.jpg","width":"592","height":"394","alt":"house","authorHref":"https://unsplash.com/@insolitus","authorName":"Rowan Heuvel","blurDataURL":"data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAADAAQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAfEAAABgIDAQAAAAAAAAAAAAAAAQIDBAUREgcIIVH/xAAVAQEBAAAAAAAAAAAAAAAAAAADBP/EABoRAAICAwAAAAAAAAAAAAAAAAACARESITH/2gAMAwEAAhEDEQA/AJ52Etp9ZyvcMwZbrLStHDQlXmxpLOPgAANXbGNl
110Uot8P//Z"}},"minutes":4},"__N_SSG":true},"page":"/series/[series]/[seriesItem]","query":{"series":"rebuilding-mercaris-search","seriesItem":"home-menu"},"buildId":"v8jrEN7x-HFMMDD4KoSpB","isFallback":false,"gsp":true,"locale":"en","locales":["en"],"defaultLocale":"en","scriptLoader":[]}</script>
110</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.