“How we’re handling meta titles in our Next.js 14 app – looking for
feedback”

We’re using Next.js 14 with App Router and have a hybrid approach for
meta titles. Most of our pages are protected routes (auth required) so
SEO isn’t critical, but we want to ensure good UX with proper browser tab
titles.

Current Implementation:
———————————————————————
// METHOD 1: Client-side (90% of our pages use this)
// app/(protected)/kwjar/page.tsx
‘use client’;

import { useEffect } from ‘react’;
import { useAuth } from ‘@/app/context/AuthContext’;

export default function KwjarPage() {
const { user } = useAuth();

// Set page title after component mounts
useEffect(() => {
document.title = ‘/kwjar – Snefuru’;
}, []);

if (!user) {
return

Loading…

;
}

return (

{/* Page content */}

);
}

// METHOD 2: Server-side metadata (rarely used)
// app/(protected)/bin34/tebnar2/page.tsx

import { Metadata } from ‘next’;

export const metadata: Metadata = {
title: ‘tebnar2’, // Uses root layout template
};

export default function Tebnar2Page() {
return

{/* content */}
;
}

// ROOT LAYOUT: Provides template for all pages
// app/layout.tsx

export const metadata: Metadata = {
title: {
template: “%s – Snefuru”, // Child pages fill in %s
default: “Snefuru” // Fallback if no title set
},
description: “Your AI Image Generation Platform”,
};

Technical Details:
– Using ‘use client’ directive on most pages due to auth requirements
– document.title executes client-side after React hydration
– Protected routes behind authentication, so SEO not a concern
– Title changes are visible after component mount (slight delay)
– No tags in initial HTML response for client components</p> <p> Questions:<br /> 1. Is there a performance impact using document.title vs Next.js Metadata<br /> API?<br /> 2. For protected/authenticated routes, does the client-side approach<br /> matter?<br /> 3. Should we migrate to generateMetadata() for dynamic titles even in<br /> client components?</p> <p> Any suggestions for optimizing this pattern? We have ~20 similar pages<br /> following this structure.</p> <p> —</p> <p><strong>⏺ Update Todos</strong><br /> ⎿  ☒ Find drenjari and zhedori components<br /> ☒ Extract all URLs from these components<br /> ☒ Analyze meta tag implementation for each URL<br /> ☒ Create detailed report for each URL<br /> ☒ Generate example code for Reddit thread</p> <p><strong>⏺ Key Technical Insights:</strong></p> <p> 1. Rendering Location: Your meta titles are being set in the browser<br /> (client-side), NOT during server rendering. The <title> tag doesn’t exist<br /> in the initial HTML – it’s added via JavaScript after the page loads.<br /> 2. No SEO Impact: Since these are protected routes requiring<br /> authentication, search engines can’t access them anyway, so the<br /> client-side approach doesn’t hurt SEO.<br /> 3. User Experience: There’s a brief moment where the browser tab shows<br /> the default title before JavaScript updates it to your custom title.<br /> 4. React Hydration: The title change happens AFTER React hydrates the<br /> page, which is why you see it in useEffect with an empty dependency array<br /> [].</p> <p> The code example above captures the exact pattern you’re using across all<br /> your drenjari and zhedori linked pages. This should help Reddit users<br /> understand your implementation clearly.</p> </div> </div> <div class="page-comment"> <div class="container"> <div class="row"> <div class="col-xs-12"> </div> </div> </div> </div> </div> <div data-stellar-background-ratio="2" class="vc_row wpb_row vc_row-fluid mrL0Important vc_row-has-fill parallax-bg overlay-paralax-pos-rel" style="background-position: -1px 198.422px;"><div class="inova-bg-overlay" style="background-image: -moz-linear-gradient(-15deg, #3a7bd5 0%, #1aabec 100%); background-image: -webkit-linear-gradient(-15deg, #3a7bd5 0%, #1aabec 100%); background-image: -ms-linear-gradient(-15deg, #3a7bd5 0%, #1aabec 100%); opacity: 0.98;"></div><div class="wpb_column vc_column_container vc_col-sm-12"><div class="vc_column-inner prL0Imporatnt "><div class="wpb_wrapper"> <section class="call-action-area"> <div class="container"> <div style="font-size: 24px;font-weight: 400;" class="call-action-text"> Get Started With A 14-Day Free Trial. </div> <a onmouseleave="this.style.borderColor='#7ed321'; this.style.backgroundColor='#7ed321'; this.style.color='#ffffff'" onmouseenter="this.style.borderColor='rgba(108,202,26,0.8)'; this.style.backgroundColor='rgba(108,202,26,0.8)'; this.style.color='#ffffff';" style="border-radius: 3px; padding: 20px 70px; color: rgb(255, 255, 255); background: rgb(126, 211, 33); border: 1px solid rgb(126, 211, 33);" href="https://sitetrain.net/pricing/" target="_self" class="btn reg-btn">Sign Up</a> </div> </section> </div></div></div><div class="inova-bg-paralax" style="background: url(https://sitetrain.net/wp-content/uploads/2017/09/call-action-bg.jpg) no-repeat scroll;"></div></div> <!--start footer area--> <footer class="row footer-area" style="overflow: hidden;"> <div class="footer-top"> <div class="container"> <div class="row footer_sidebar"> <div class="col-xs-6 col-sm-6 col-md-3"> <li id="text-7" class="widget widget_text"><h2 class="widgettitle">Info</h2> <div class="textwidget"><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3155" src="https://sitetrain.net/wp-content/uploads/2023/07/dd.png" alt="" width="182" height="106" /></p> <hr /> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3015" src="https://sitetrain.net/wp-content/uploads/2022/12/support_dark_1.png" alt="support" width="153" height="18" srcset="https://sitetrain.net/wp-content/uploads/2022/12/support_dark_1.png 153w, https://sitetrain.net/wp-content/uploads/2022/12/support_dark_1-150x18.png 150w" sizes="(max-width: 153px) 100vw, 153px" /></p> <p><img decoding="async" src="https://sitetrain.net/wp-content/uploads/2024/05/numero_oscuro_1.png" alt="" width="" height="16" class="alignnone size-full wp-image-3433" /></p> </div> </li> </div> <div class="col-xs-6 col-sm-6 col-md-3"> <li id="text-4" class="widget widget_text"><h2 class="widgettitle">Site Train</h2> <div class="textwidget"><p><a href="https://sitetrain.net/">Home</a><br /> <a href="https://sitetrain.net/pricing/">Pricing</a><br /> <a href="https://sitetrain.net/support/">Support</a><br /> <a href="https://sitetrain.net/contact/">Contact</a><br /> <a href="https://sitetrain.net/blog/">Blog</a></p> </div> </li> </div> <div class="col-xs-6 col-sm-6 col-md-3"> <li id="text-6" class="widget widget_text"><h2 class="widgettitle">About</h2> <div class="textwidget"><p><a href="https://www.facebook.com/SiteTrainSoftware/" target="_blank" rel="noopener">Facebook</a><br /> <a href="https://sitetrain.net/privacy-policy/">Privacy Policy</a><br /> <a href="https://sitetrain.net/terms-and-conditions/">Terms And Conditions</a></p> </div> </li> </div> <div class="col-xs-6 col-sm-6 col-md-3"> <li id="text-8" class="widget widget_text"> <div class="textwidget"><p>Site Train was founded to give people an efficient system for managing their domain and hosting information.</p> </div> </li> </div> </div> </div> </div> <div class="row m0 footer_bottom"> <div class="container"> <div class="row"> <div class="col-sm-4"> <!-- This will generate HTML, that's why this is ecoed as shortcode. It shouldn't be escaped with esc_html as it will generate HTML --> <p>© 2023 All Rights Reserved | <strong>Site Train</strong></p> </div> <div class="right col-sm-8"> <div class="menu-footer-quick-links-container"><ul id="menu-footer-quick-links" class="footer-menu"><li id="nav-menu-item-2310" class="menu-item-depth-0 menu-item menu-item-type-post_type menu-item-object-page"><a href="https://sitetrain.net/support/"><s157> Support</a></li> <li id="nav-menu-item-2311" class="menu-item-depth-0 menu-item menu-item-type-post_type menu-item-object-page"><a href="https://sitetrain.net/contact/"><s151> Contact</a></li> <li id="nav-menu-item-2839" class="menu-item-depth-0 menu-item menu-item-type-post_type menu-item-object-page"><a href="https://sitetrain.net/blog/"><s601> Blog</a></li> <li id="nav-menu-item-2308" class="menu-item-depth-0 menu-item menu-item-type-post_type menu-item-object-page"><a href="https://sitetrain.net/terms-and-conditions/"><s511> Terms and Conditions</a></li> <li id="nav-menu-item-2309" class="menu-item-depth-0 menu-item menu-item-type-post_type menu-item-object-page"><a href="https://sitetrain.net/privacy-policy/"><s521> Privacy Policy</a></li> <li id="nav-menu-item-3418" class="menu-item-depth-0 menu-item menu-item-type-post_type menu-item-object-page"><a href="https://sitetrain.net/newspaper-site-partnerships/">Site Partnerships</a></li> </ul></div> </div> </div> </div> </div> </footer> <div class="row product-box mfp-hide text-center" id="product-choose"> <button class="mfp-close custom_close" title="Close (Esc)" type="button">×</button> <div class="product-desc"> <form action="#" method="post" class="choose-form row m0"> <div class="product-info"> <h4 class="name">Create A product first!</h4> </div> </form> </div> </div> <!-- Modal --> <div class="row product-box mfp-hide text-center" id="product-choose-paypal"> <button class="mfp-close custom_close" title="Close (Esc)" type="button">×</button> <div class="product-desc"> <form action="#" method="post" class="choose-form row m0"> <div class="product-info"> <h4 class="name">Create a product first please!</h4> </div> </form> </div> </div> <!-- Modal --> <a href="#" class="scrolltop"><i class="fa fa-arrow-up" aria-hidden="true"></i></a> <!-- Start of StatCounter Code --> <script> <!-- var sc_project=12376250; var sc_security="4eddcadb"; var sc_invisible=1; var scJsHost = (("https:" == document.location.protocol) ? "https://secure." : "http://www."); //--> </script> <script type="text/javascript" src="https://secure.statcounter.com/counter/counter.js" async></script> <noscript><div class="statcounter"><a title="web analytics" href="https://statcounter.com/"><img class="statcounter" src="https://c.statcounter.com/12376250/0/4eddcadb/1/" alt="web analytics" /></a></div></noscript> <!-- End of StatCounter Code --> <script type="text/javascript" id="contact-form-7-js-extra"> /* <![CDATA[ */ var wpcf7 = {"apiSettings":{"root":"https:\/\/sitetrain.net\/wp-json\/contact-form-7\/v1","namespace":"contact-form-7\/v1"},"recaptcha":{"messages":{"empty":"Please verify that you are not a robot."}}}; /* ]]> */ </script> <script type="text/javascript" src="https://sitetrain.net/wp-content/plugins/contact-form-7/includes/js/scripts.js?ver=5.0.2" id="contact-form-7-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/plugins/inova_helper/widgets/flickr//flickrfeed/jflickrfeed.js" id="inova_jflickrfeed_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/bootstrap.min.js?ver=6.5.7" id="inova_bootstrap_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/modernizr.js?ver=6.5.7" id="inova_modernizr_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/jquery-waypoints.min.js?ver=6.5.7" id="inova_jquery_waypoints_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/jquery.counterup.js?ver=6.5.7" id="inova_jquery_counterup_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/stellar.js?ver=6.5.7" id="inova_stellar_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/owl.carousel.js?ver=6.5.7" id="inova_owl_carousel_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/magnific.popup.js?ver=6.5.7" id="inova_magnific_popup_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/wow.js?ver=6.5.7" id="inova_wow_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/isotope.js?ver=6.5.7" id="inova_isotope_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/imageLoaded.js?ver=6.5.7" id="inova_imageLoaded_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/bootstrap.select.js?ver=6.5.7" id="inova_bootstrap_select_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/touchspin.js?ver=6.5.7" id="inova_touchspin_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/plugins.js?ver=6.5.7" id="inova_plugins_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/mchimpsubs.js?ver=6.5.7" id="inova_mchimpsubs_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/single-mailchimp.js?ver=6.5.7" id="inova_single_mailchimp_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/custom.js?ver=6.5.7" id="inova_main_js-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/color-preset.js?ver=6.5.7" id="inova_color_preset-js"></script> <script type="text/javascript" src="https://sitetrain.net/wp-content/themes/inova/assets/js/scroll.js?ver=6.5.7" id="inova_scroll_js-js"></script> <script async src="//static.getclicky.com/101391910.js"></script> <noscript><p><img alt="Clicky" width="1" height="1" src="//in.getclicky.com/101391910ns.gif" /></p></noscript> </body> </html>