- Scan ID:
- 9e60af3f-0335-48ba-af6f-2f61d4e6c687Finished
- Submitted URL:
- https://blog.thoughtspile.tech/2021/10/27/better-react-context/Redirected
- Report Finished:
Links · 4 found
The outgoing links identified from the page
Link | Text |
---|---|
https://github.com/dai-shi/react-tracked | react-tracked |
https://www.linkedin.com/in/vklepov/ | |
https://github.com/thoughtspile | |
https://buymeacoffee.com/thoughtspile | Hello, friend! My name is Vladimir, and I love writing about web development. If you got down here, you probably enjoyed this article. My goal is to become an independent content creator, and you'll help me get there bybuying me a coffee! |
JavaScript Variables · 3 found
Global JavaScript variables loaded on the window object of a page, are variables declared outside of functions and accessible from anywhere in the code within the current scope
Name | Type |
---|---|
onbeforetoggle | object |
documentPictureInPicture | object |
onscrollend | object |
Console log messages · 3 found
Messages logged to the web console
Type | Category | Log |
---|---|---|
warning | other |
|
error | network |
|
error | network |
|
HTML
The raw HTML body of the page
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="HandheldFriendly" content="True"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="yandex-verification" content="635edef3908320be"><meta name="google-site-verification" content="5nfOA5k_QcuAP9zbAASIV24DlAZG7BWxRVYoNlKCAoc"><meta name="color-scheme" content="dark light"><meta property="og:type" content="article"><meta property="og:title" content="Why I always wrap Context.Provider and useContext"><meta property="og:url" content="https://thoughtspile.github.io/2021/10/27/better-react-context/"><meta property="og:site_name" content="Vladimir Klepov as a Coder"><meta property="og:locale" content="en_US"><meta property="og:image" content="https://thoughtspile.github.io/images/wrapped-context.jpg"><meta property="article:published_time" content="2021-10-27T00:00:00.000Z"><meta property="article:author" content="Vladimir Klepov"><meta property="article:tag" content="react"><meta property="article:tag" content="programming"><meta property="article:tag" content="frontend"><meta property="article:tag" content="hooks"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:creator" content="@thoughtspile"><meta name="twitter:image" content="https://thoughtspile.github.io/images/wrapped-context.jpg"><title>Why I always wrap Context.Provider and useContext</title><link rel="stylesheet" href="/css/style.css?ts=1715245757403"><link rel="canonical" href="https://thoughtspile.github.io/2021/10/27/better-react-context/"><link rel="alternate" href="/atom.xml" title="Vladimir Klepov as a Coder" type="application/atom+xml"><script defer="defer" data-domain="thoughtspile.github.io" src="https://t.thoughtspile.tech/js/script.js"></script></head><body class="max-width mx-auto px3"><div class="content index"><a href="/" id="header" class="header header--post"><div id="logo"></div><header id="title"><h1>Vladimir Klepov as a Coder</h1></header></a><article class="post" itemscope="" itemtype="http://schema.org/BlogPosting"><header><h1 class="posttitle" itemprop="name headline">Why I always wrap Context.Provider and useContext</h1><div class="post__date">Written in <time>2021</time></div></header><div class="content" itemprop="articleBody"><p>React context is a cool feature, and I use it a lot for injecting configuration and making container / child component APIs (think <code><RadioGroup /> + <RadioButton /></code>). Unfortunately, out of the box Context comes with a limiting and not very convenient API. In most cases, I choose to wrap both the provider and consumer with a custom component and a hook. Some of the issues I highlight are more relevant to library maintainers, but most apply to app development as well.</p><p>In this post, we revisit an <code>AdaptivityContext</code> that allows components to read viewport dimension data — pixel <code>width</code> and breakpoint status, <code>isMobile</code>:</p><pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">getWidth</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> window<span class="token punctuation">.</span>innerWidth<span class="token punctuation">;</span><br><span class="token keyword">const</span> <span class="token function-variable function">isMobile</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">w</span><span class="token operator">:</span> number</span><span class="token punctuation">)</span> <span class="token operator">=></span> w <span class="token operator"><</span> <span class="token number">600</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> AdaptivityContext <span class="token operator">=</span> <span class="token function">createContext</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">w</span><span class="token operator">:</span> <span class="token function">getWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> <span class="token literal-property property">isMobile</span><span class="token operator">:</span> <span class="token function">isMobile</span><span class="token punctuation">(</span>getWidth<span class="token punctuation">)</span><span class="token punctuation">,</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>If you've read my <a href="/2021/10/04/react-context-dangers/">post on Context performance issues,</a> you know it is not the best design choice — components that only care about <code>isMobile</code> will still re-render on every <code>width</code> change. Still, suppose that's what we happen to have on our project. How can custom <code>AdaptivityProvider</code> and <code>useAdaptivity</code> help us?</p><p><img src="/images/wrapped-context.jpg" alt=""></p><h2>Wrap useContext</h2><p>In raw context API, the consuming components utilize <code>useContext</code> hook (or a <code>Context.Consumer</code> component, but I don't know why anyone would choose it over the hook today). There's nothing especially wrong with <code>useContext</code>, but we can do so much better with a custom <code>useAdaptivity</code>!</p><p>If <code>useContext</code> is used outside <code>Provider</code>, you're left with either a static default value from <code>createContext</code> or cryptic <em>can't read property width of null</em> errors. Sometimes it's enough, but <code>AdaptivityContext</code> is supposed to be dynamic, and we get a lot of "bug reports" that are fixed with a "did you forget the provider?". A custom <code>useAdaptivity</code> gives us two stronger options:</p><ol><li>Show an explicit error message, like <code>console.error('useAdaptivity must be used inside AdaptivityProvider')</code></li><li>Give each component an independent size observer, and make <code>AdaptivityProvider</code> optional for advanced optimizations and overrides.</li></ol><p>Next, <code>useContext</code> has a 1:1 relationship to contexts. Fixing <code>AdaptivityContext</code> performance problems involves splitting it into two separate contexts — a frequently-changing one for <code>width</code>, and a more stable one for <code>isMobile</code>. <code>useAdaptivity</code> can subscribe to both contexts — it won't have any performance benefits, but it's backwards compatible and allows users to gradually update their apps to the new API:</p><pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">useAdaptivity</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">'Please migrate to useMobile or useViewport for better performance'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> viewport <span class="token operator">=</span> <span class="token function">useContext</span><span class="token punctuation">(</span>ViewportContext<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> mobile <span class="token operator">=</span> <span class="token function">useContext</span><span class="token punctuation">(</span>MobileContext<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span>viewport<span class="token punctuation">,</span> <span class="token operator">...</span>mobile <span class="token punctuation">}</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre><p>Custom <code>useAdaptivity</code> hook also allows for an alternate context injection mechanism, like <a href="https://github.com/dai-shi/react-tracked" target="_blank" rel="noopener">react-tracked</a>. You can even bind to a global state manager instead of context. Nothing about <code>useAdaptivity</code> implies that it has anything to do with contexts!</p><p>So, a custom <code>useAdaptivity</code> hook gives us a lot of freedom — we can modify the contexts as we wish, replace them with other state management mechanism, and we can handle a missing provider as we see fit. That's convincing. What about <code>Provider</code>?</p><h2>Wrap Context.Provider, too</h2><p><code>React.createContext</code> gives you a <code>Context.Provider</code> component you're supposed to use for passing a context value. It lacks some important features, but we can easily fix that by wrapping it into a custom <code>Provider</code> component.Frankly, it's less of a concern than <code>useContext</code> — you often have a single <code>Provider</code>, and it has to be located in <em>some</em> component, so you can't go too wrong. For completeness, here's what I normally do with a custom <code>Provider</code>.</p><p>Raw <code>Context.Provider</code> with object context is a performance hazard — if you don't stabilize <code>value</code> reference yourself, every context consumer will re-render on every <code>Provider</code> render, because React updates them every time context value changes under strict equality. I don't know why this feature is not in react core, but it's one good reason to have a custom provider (see my <a href="/2021/04/05/useref-usememo/">post on custom memo</a> for details on <code>useObjectMemo</code>):</p><pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">AdaptivityProvider</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children<span class="token punctuation">,</span> <span class="token operator">...</span>context <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> contextValue <span class="token operator">=</span> <span class="token function">useObjectMemo</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token punctuation">(</span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">AdaptivityContext.Provider</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>contextValue<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text"><br> </span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token plain-text"><br> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">AdaptivityContext.Provider</span></span><span class="token punctuation">></span></span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre><p>Just like <code>useContext</code>, raw <code>Providers</code> have a 1:1 relationship with contexts, making it harder to split / merge the contexts. To fix the coupling of <code>width</code> and <code>isMobile</code> updates, we must split <code>AdaptivityContext</code> into two parts. Easy with a custom provider:</p><pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">AdaptivityProvider</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> children<span class="token punctuation">,</span> width<span class="token punctuation">,</span> isMobile <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> viewportValue <span class="token operator">=</span> <span class="token function">useObjectMemo</span><span class="token punctuation">(</span><span class="token punctuation">{</span> width <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> mobileValue <span class="token operator">=</span> <span class="token function">useObjectMemo</span><span class="token punctuation">(</span><span class="token punctuation">{</span> isMobile <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token punctuation">(</span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">ViewportSizeContext.Provider</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>viewportValue<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text"><br> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">MobileContext.Provider</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>mobileValue<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text"><br> </span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token plain-text"><br> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">MobileContext.Provider</span></span><span class="token punctuation">></span></span><span class="token plain-text"><br> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">ViewportSizeContext.Provider</span></span><span class="token punctuation">></span></span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre><p>Just like <code>useAdaptivity</code>, <code>AdaptivityProvider</code> also allows you to replace context with any other state management technology — just throw a <code><StoreProvider></code> in there and you're done.</p><p>Finally, a custom provider can handle context value in a smarter way — add default options or merge with another provider up the tree. If we had both <code>width</code> and <code>height</code>, we could allow partial overrides — user could use <code><ViewportSizeProvider width={100}></code> in a narrow sidebar, while preserving the <code>height</code> value:</p><pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> parentViewport <span class="token operator">=</span> <span class="token function">useContext</span><span class="token punctuation">(</span>ViewportSizeContext<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> contextValue <span class="token operator">=</span> <span class="token function">useObjectMemo</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token operator">...</span>parentWiewport<span class="token punctuation">,</span><br> <span class="token operator">...</span>size<br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Of course, you could also have a custom mechanism of auto-detecting and updating context values:</p><pre class="language-jsx"><code class="language-jsx"><span class="token function">useLayoutEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> <span class="token function-variable function">cb</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token function">setDetectedSize</span><span class="token punctuation">(</span><span class="token function">getViewportSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">;</span><br> <span class="token function">cb</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'resize'</span><span class="token punctuation">,</span> cb<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> window<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'resize'</span><span class="token punctuation">,</span> cb<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> contextValue <span class="token operator">=</span> <span class="token function">useObjectMemo</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token operator">...</span>detectedSize<span class="token punctuation">,</span><br> <span class="token operator">...</span>props<br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>You could have amazing combinations of inheritance, auto-detection and overrides. Really, there are endless possibilities once you are the master of your context provider. Just don't settle for raw <code>Context.Provider</code>.</p><hr><p>Wrapping both the provider and the consumer of a context into custom hooks gives you a lot of flexibility:</p><ul><li>Merge and split context as you want.</li><li>Replace raw contexts with another state injection technique.</li><li>Stabilize context object value.</li><li>Introduce smart dynamic defaults for context value.</li><li>Inherit from other providers up the tree with partial overrides.</li><li>Warn or fallback on missing provider.</li></ul><p>This flexibility is crucial if you're building a library, but it also helps a lot in any non-trivial app. Hope that convinces you! See you later.</p></div><span class="share"><div><a href="https://twitter.com/share?url=https://thoughtspile.github.io/2021/10/27/better-react-context/&text=Why I always wrap Context.Provider and useContext by @thoughtspile" target="_blank" rel="noopener">Tweet</a></div><div id="share-button" style="display: none;"><a>Share</a></div></span><div class="social-links"><a href="https://www.linkedin.com/in/vklepov/" class="link--bare" target="_blank" rel="noopener"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="linkedin" class="svg-inline--fa fa-linkedin fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"></path></svg></a><a href="https://github.com/thoughtspile" class="link--bare" target="_blank" rel="noopener"><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></a><a href="/atom.xml" class="link--bare" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="rss" class="svg-inline--fa fa-rss fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 64C0 46.3 14.3 32 32 32c229.8 0 416 186.2 416 416c0 17.7-14.3 32-32 32s-32-14.3-32-32C384 253.6 226.4 96 32 96C14.3 96 0 81.7 0 64zM128 416c0 35.3-28.7 64-64 64s-64-28.7-64-64s28.7-64 64-64s64 28.7 64 64zM32 160c159.1 0 288 128.9 288 288c0 17.7-14.3 32-32 32s-32-14.3-32-32c0-123.7-100.3-224-224-224c-17.7 0-32-14.3-32-32s14.3-32 32-32z"></path></svg></a></div><a href="https://buymeacoffee.com/thoughtspile" class="link--bare bmc-card" target="_blank" rel="noopener"><div><b>Hello, friend!</b> My name is Vladimir, and I love writing about web development. If you got down here, you probably enjoyed this article. My goal is to become an independent content creator, and you'll help me get there by <span class="bmc">buying me a coffee!</span></div><img src="/images/bmc.png" data-no-preview="" class="bmc-image"></a><div class="post-related"><div class="post-sibling"><span class="post-siblings__direction">More?</span> <a class="tag-link-link" href="/">All articles ever</a> <a class="tag-link-link" href="/tags/react" rel="tag">react</a> <a class="tag-link-link" href="/tags/programming" rel="tag">programming</a> <a class="tag-link-link" href="/tags/frontend" rel="tag">frontend</a> <a class="tag-link-link" href="/tags/hooks" rel="tag">hooks</a></div><a class="link--bare post-sibling" href="/2021/10/25/useref-no-current/"><span class="post-siblings__direction">Older?</span> <span class="link__text">Can we useRef, but without the .current? Let's try!</span> </a><a class="link--bare post-sibling" href="/2021/11/08/semver-challenges/"><span class="post-siblings__direction">Newer?</span> <span class="link__text">SemVer: The Tricky Parts</span></a></div></article><script async="" src="/js/share.js"></script></div></body></html>