Eduardo San Martin Morote Frontend Nerd @posva πŸ”— esm.dev

Modern Routing

Modern Routing

Modern Routing

Modern Frontend Routing

https://movies.com/search?genre=horror&page=1#filters

https://movies.com/search?genre=horror&page=1#filters History

https://movies.com/search?genre=horror&page=1#filters History Router

https://movies.com/search?genre=horror&page=1#filters History Matcher Router

https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components

https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components

History API history.pushState(data, title, url) history.replaceState(data, title, url) window.addEventListener(β€˜popstate’, handler) history.state history.length posva

https://movies.com history.state history.length null 1 posva

https://movies.com history.state history.length null 1 posva

https://movies.com history.state history.length null 1 history.pushState({ from: null }, ”, β€˜/releases’) posva

https://movies.com /releases history.state history.length { from: null } 2 history.pushState({ from: null }, ”, β€˜/releases’) posva

https://movies.com /releases history.state history.length { from: null } 2 history.pushState({ from: null }, ”, β€˜/releases’) history.pushState({ from: β€˜/releases’ }, ”, β€˜/search?genre=horror&page=1#filters’) posva

https://movies.com /search?genre=horror&page=1#filters history.state history.length { from: β€˜/releases’ } 3 history.pushState({ from: null }, ”, β€˜/releases’) history.pushState({ from: β€˜/releases’ }, ”, β€˜/search?genre=horror&page=1#filters’) posva

https://movies.com /search?genre=horror&page=1#filters history.state history.length { from: β€˜/releases’ } 3 history.pushState({ from: null }, ”, β€˜/releases’) history.pushState({ from: β€˜/releases’ }, ”, β€˜/search?genre=horror&page=1#filters’) posva

https://movies.com /releases history.state history.length { from: null } 3 history.pushState({ from: null }, ”, β€˜/releases’) history.pushState({ from: β€˜/releases’ }, ”, β€˜/search?genre=horror&page=1#filters’) posva

https://movies.com /releases history.state history.length { from: null } 3 history.pushState({ from: null }, ”, β€˜/releases’) history.pushState({ from: β€˜/releases’ }, ”, β€˜/search?genre=horror&page=1#filters’) posva

https://movies.com /search?genre=horror&page=1#filters history.state history.length { from: β€˜/releases’ } 3 history.pushState({ from: null }, ”, β€˜/releases’) history.pushState({ from: β€˜/releases’ }, ”, β€˜/search?genre=horror&page=1#filters’) posva

https://movies.com/search?genre=horror&page=1#filters posva

https://movies.com/search?genre=horror&page=1#filters Path Query (or search) Hash (or fragment) posva

Path /search Query ?genre=horror&page=1 Hash #filters posva

Path /search Query ?genre=horror&page=1 Hash #filters location posva

Path /search Query ?genre=horror&page=1 Hash #filters location.pathname posva

Path /search Query ?genre=horror&page=1 Hash #filters location.search posva

Path /search Query ?genre=horror&page=1 Hash #filters location.hash posva

Path /search Query ?genre=horror&page=1 Hash #filters location.href posva

/search?genre=horror&page=1#filters posva

/search?genre=horror&page=1#filters { } path: β€˜/search’, query: { genre: β€˜horror’, page: β€˜1’ }, hash: β€˜#filters’ posva

?genre=horror&page=1 posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

URLSearchParams API const q = new URLSearchParams(β€˜genre=horror&page=1’) for (const key of q.keys()) { q.getAll(key) } posva

posva

Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C Γ± U+00F1 %C3%B1 posva

Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C Γ± U+00F1 %C3%B1

<p class=”a”> posva

Encoding Character Hex Encoded ” U+0022 %22 < U+003C %3C Γ± U+00F1 %C3%B1

<p class=”a”> %3Cp%20class=%22a%22%3E posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path Query Hash posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path Query Hash ␣ ” < > ` posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path ␣ ” < > Query Hash ␣ " < > posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path ␣ ” < > # ? { } Query Hash ␣ " < > posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path ␣ ” < > # ? { } Query ␣ " < > Hash ␣ ” < > ` posva

Encoding Non-printable characters: U+0000 (NULL) to U+001F (US) and U+007F (DEL) Any non-ascii character: > U+007E (~) The percentage character: U+0025 (%) Path Query Hash ␣ ” < > # ? { } ␣ " < > # & = ␣ " < > posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>`’ posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>` posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>` location.hash: β€˜#%20%22%3C%3E%60’ posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>` location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># β€œ<>` posva

location.hash = β€˜# β€œ<>' location.hash = encodeURI('# "<>’) location.hash: β€˜# β€œ<>' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># β€œ<>` posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20"<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># β€œ<>` posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash = encodeURI('# "<>’) location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># β€œ<>` posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' location.hash = encodeURI('# "<>’) location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..># β€œ<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20"<>%60 location.hash: '# "<>’ https://<…..># β€œ<>` posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

location.hash = β€˜# β€œ<>' πŸ€” location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

location.hash = β€˜# β€œ<>' πŸ€” location.hash: β€˜# "<>’ πŸ’― location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

location.hash = β€˜# β€œ<>' πŸ€” location.hash: β€˜# "<>’ πŸ’― location.hash: β€˜#%20%22%3C%3E%60’ βœ… location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>https://<…..># "<> https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

location.hash = β€˜# β€œ<>' πŸ€” location.hash: β€˜# "<>’ πŸ’― location.hash: β€˜#%20%22%3C%3E%60’ βœ… location.hash: β€˜#%20%22%3C%3E%60’ πŸ€ͺ location.hash: β€˜# β€œ<>' https://<…..>#%20"<> https://<…..># β€œ<>https://<…..>#%20"<>%60 https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

location.hash = β€˜# β€œ<>' location.hash: β€˜# "<>’ https://<…..>#%20”<>location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜# β€œ<>' https://<…..># "<> location.hash = encodeURI(β€˜# β€œ<>') location.hash: β€˜#%20%22%3C%3E%60' https://<…..>#%20%22%3C%3E%60 location.hash: β€˜#%20%22%3C%3E%60' https://<…..># "<> location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20”<>%60 location.hash: β€˜#%20%22%3C%3E%60’ https://<…..>#%20%22%3C%3E%60 posva

Encode you URL even if it looks bad encodeURI / decodeURI encodeURIComponent / decodeURIComponent Path and hash Query keys and values posva

History { } path: β€˜/movies/493922’, query: {}, hash: ” Communicate with the address bar Handle encoding Handle browser specific bugs/inconsistencies posva

{ { } path: β€˜/movies/493922’, query: {}, hash: ” } path: β€˜/movies/493922’, params: { id: β€˜493922’}, name: β€˜MovieDetail’, component: …, query: {}, hash: ” posva

Matcher { { } path: β€˜/movies/493922’, query: {}, hash: ” } path: β€˜/movies/493922’, params: { id: β€˜493922’}, name: β€˜MovieDetail’, component: …, query: {}, hash: ” posva

Matcher β€˜/home’ /^/home$/i /^/movies/([^/]+)$/i β€˜/movies/:id’ /^/movies/(\n+)$/i β€˜/movies/:id(\n+)’ /^/movies/new$/i β€˜/movies/new’ path-to-regexp posva

Matcher β€˜/home’ β€˜/movies/:id’ β€˜/movies/:id(\n+)’ β€˜/movies/new’ /^/home$/i β€˜/movies/new’ /^/movies/([^/]+)$/i /^/movies/(\n+)$/i /^/movies/new$/i posva

Matcher β€˜/home’ β€˜/movies/:id’ β€˜/movies/:id(\n+)’ β€˜/movies/new’ /^/home$/i β€˜/movies/new’ /^/movies/([^/]+)$/i /^/movies/(\n+)$/i /^/movies/new$/i posva

Matcher β€˜/home’ β€˜/movies/:id’ β€˜/movies/:id(\n+)’ β€˜/movies/new’ /^/home$/i /^/movies/([^/]+)$/i β€˜/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Matcher β€˜/home’ β€˜/movies/:id’ β€˜/movies/:id(\n+)’ β€˜/movies/new’ /^/home$/i /^/movies/([^/]+)$/i β€˜/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Matcher β€˜/home’ β€˜/movies/:id’ β€˜/movies/:id(\n+)’ β€˜/movies/new’ /^/home$/i /^/movies/([^/]+)$/i β€˜/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Matcher 70 β€˜/home’ 130 β€˜/movies/:id’ 135 β€˜/movies/:id(\n+)’ 140 β€˜/movies/new’ /^/home$/i /^/movies/([^/]+)$/i β€˜/movies/new’ /^/movies/(\n+)$/i /^/movies/new$/i posva

Matcher 140 β€˜/movies/new’ 135 β€˜/movies/:id(\n+)’ 130 β€˜/movies/:id’ 70 β€˜/home’ β€˜/movies/new’ posva

Matcher 140 β€˜/movies/new’ 135 β€˜/movies/:id(\n+)’ 130 β€˜/movies/:id’ 70 β€˜/home’ β€˜/movies/new’ posva

Matcher 140 β€˜/movies/new’ 135 β€˜/movies/:id(\n+)’ 130 β€˜/movies/:id’ 70 β€˜/home’ posva

Matcher 140 β€˜/movies/new’ 135 β€˜/movies/:id(\n+)’ 130 β€˜/movies/:id’ 70 β€˜/home’ 70 posva

Matcher 140 β€˜/movies/new’ 135 β€˜/movies/:id(\n+)’ 130 β€˜/movies/:id’ 70 β€˜/home’ 70 + 70 70 posva

Matcher 140 β€˜/movies/new’ 70 + 70 135 β€˜/movies/:id(\n+)’ 130 β€˜/movies/:id’ 70 + 60 70 β€˜/home’ 70 posva

Matcher 140 β€˜/movies/new’ 70 + 70 135 β€˜/movies/:id(\n+)’ 70 + 60 130 β€˜/movies/:id’ 70 + 60 70 β€˜/home’ 70 + 5 posva

140 70 + 70 135 70 + 60 130 70 + 60 70 70 β€˜/movies/new’ + 5 posva

Router { } path: β€˜/movies/new’, params: {}, name: β€˜MovieNew’, component: …, query: {}, hash: ” posva

Router { } path: β€˜/movies/new’, params: {}, name: β€˜MovieNew’, component: …, query: {}, hash: ” View Component posva

Router { } path: β€˜/movies/new’, params: {}, name: β€˜MovieNew’, component: …, query: {}, hash: ” View Component posva

https://movies.com/search?genre=horror&page=1#filters History Matcher Router Components posva

Thank you! posva