Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      May 16, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      May 16, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      May 16, 2025

      How To Prevent WordPress SQL Injection Attacks

      May 16, 2025

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025

      Bing Search APIs to be “decommissioned completely” as Microsoft urges developers to use its Azure agentic AI alternative

      May 16, 2025

      Microsoft might kill the Surface Laptop Studio as production is quietly halted

      May 16, 2025

      Minecraft licensing robbed us of this controversial NFL schedule release video

      May 16, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      The power of generators

      May 16, 2025
      Recent

      The power of generators

      May 16, 2025

      Simplify Factory Associations with Laravel’s UseFactory Attribute

      May 16, 2025

      This Week in Laravel: React Native, PhpStorm Junie, and more

      May 16, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025
      Recent

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025

      Bing Search APIs to be “decommissioned completely” as Microsoft urges developers to use its Azure agentic AI alternative

      May 16, 2025

      Microsoft might kill the Surface Laptop Studio as production is quietly halted

      May 16, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»Dev Tutorial: Building a Shared Navigation With Help from AEM Content Services | Part 2

    Dev Tutorial: Building a Shared Navigation With Help from AEM Content Services | Part 2

    June 26, 2024

    In part 1 of this series, I walked through the setup of a simple navigation component (HTL). This component’s model uses a service to provide a list of navigation link elements. This results in the author only needing to select a starting path, without having to add each link manually. What’s more, we set up the component so that upon saving the dialog, a servlet is invoked to write the generated navigation links to a content fragment (along with other authored fields).  

    Here in part 2, we will step outside of AEM and see how we can use this content fragment in a React navigation component. The goal is to have shared navigation elements between the AEM site and React app.  

    You can find the project code here. 

    React Project Setup

    This is a NodeJS app, so it needs to be installed along with NPM (at least version 5.2.0 or newer). I wanted this to be a minimal setup, so npx was used to create the project as it’s now bundled with npm. If this were a project destined for production, I would advise setting this up using vite (a top-choice build tool for React and other related framework-based projects). 

    With NodeJS and NPM available, the following should be run from a terminal/CLI:  

    npx create-react-app aem-navigation-app
    cd aem-navigation-app

    Axios is used to retrieve the headless navigation content from AEM. I would usually go with the built-in fetch function for this, but Axios has some advantages for the use case, namely its automatic data conversion to JSON and a simpler response object. React-router-dom is used to render the response as links. React-router-dom and Axios were installed within the project via: 

    npm –i -S react-router-dom axios

    Let’s look at the app and navigation component, file by file. 

    React Code

    Building the Component

    Index.html 

    When the project is initialized, the id in the div container for the app is “root”. This is too generic for my taste. It was changed to “nav-container”. 

    <div id=”nav-container”></div>

    The title tag value was also changed. If you build this, you may want to change meta tags and other elements. 

    Index.js 

    Changing the id in the HTML requires corresponding changes in the index script. I changed references from root to container. Importantly the value in document.getElementById to “nav-container”. 

    const container = ReactDOM.createRoot(document.getElementById(‘nav-container’));

    container.render(

    <React.StrictMode>

    <App />

    </React.StrictMode>

    );

    Client.js 

    AEM content services data may be accessed by multiple components. It’s ideal to put the request code into a dedicated utility function. 

    A folder was created in the root of the project called “utils”, in that folder the client.js file contains a function for making calls to AEM. It accepts 3 parameters, an endpoint, any headers that need to be passed, and a boolean indicating if the request includes credentials or not. 

    import axios from ‘axios’;

    export async function getTheData(endpoint, customHeaders = {}, credentials) {

    try {

    const response = await axios.get(endpoint, {

    headers: {

    …customHeaders,

    },

    withCredentials: credentials,

    });

    return response.data;

    } catch (error) {

    throw new Error(`Client could not retrieve data: ${error.message}`);

    }

    }

    The endpoint passed to this function points to a GraphQL persisted query, stored (cacheable) in AEM (http://localhost:4502/graphql/execute.json/perficientpocs/get-logo-and-nav-links). This follows the best practice of storing and running GraphQL queries service side. The client app simply makes a request for the content. Here is the query: 

    {

    navigationCfModelList(filter: {_variation: {_expressions: [{value: “master”, _ignoreCase: true}]}})

    {

    items {

    brandLogo {

    … on ImageRef {

    _path

    }

    }

    generatedNavigationList

    }

    }

    }

    An authentication header is also passed to the getTheData function, this header should not be necessary for content that is publicly exposed through dispatcher and CDN layers. It’s only needed for this sample app. 

    Navigation.js 

    A folder was created in the root of the project called “components”. In that folder, the Navigation.js file contains the navigation component. This leverages the useState and useEffect hooks to set state variables related to the AEM request and setup that request.  

    It uses the utility function in client.js from above and then renders the response by mapping the navigation elements from the response JSON to list elements: 

    const result = await getTheData(endpoint, headers, true);

    …{data.data.navigationCfModelList.items[0].generatedNavigationList.map((item) => (

    <li className=”app-menu-item” key={item.id}>

    <Link to={item.path} className=”app-menu-link” style={{ textDecoration: ‘none’ }}>{item.title}</Link>

    </li>

    ))}

    Note, that this uses React Router Link elements instead of anchor elements. 

    Along with the navigation links, the logo is retrieved from the AEM service response and rendered in the app header. 

    App.js 

    Everything comes together in the main app script. The Navigation component is included, and the shared links are rendered alongside app-specific ones:

    <Router>

    <header>

    <Navigation />

    </header>

    </Router>

    Other files 

    I added some component CSS and an Article component just to add some sample content to our app.  

    Rendered React App

     Here is what the app looks like in my local instance: 

    If you read part 1 of this series, this should look familiar. As mentioned, the app uses the same logo as the navigation component in the AEM site. Also, see that the shared navigation elements are available in line with the app’s specific links: 

    When the logo or any of the page links are updated in AEM and published, if the navigation component is also updated and published, the app navigation component is updated automatically: 

    Wins and API considerations

    With this setup, the following have been achieved. 

    AEM content is used across channels 
    Consistent branding is maintained across the website and app 
    App-specific elements are rendered cohesively with shared elements 

    Much of the work to enable the AEM service is done for us, via out-of-the-box JSON serialization.  

    Any good service API provides a stable contract indicating how it should be used, and how content should be requested. This is important to consider when building out content fragment models.  

    For a project like the one in this series, changing fragment model structures down the road may break existing content in consuming channels, as the service response JSON would also change. For this reason, careful thought should be given early in a project to the desired state of its information architecture. 

    Take time to build out content models and schemas based on them before implementing them in components. It will promote a better, well-planned, and executed project. 

    Closing Thoughts

    This concludes the series on shared navigation. My goal was to highlight the power of AEM content services in a practical manner. I hope it was inspiring and sparked some ideas for how to share content across channels. I encourage you to share those ideas in the comments! 

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleHow To Make A Strong Case For Accessibility
    Next Article Part 2: Read and Validate PDF Text Content in Browser Using PDFBox and Selenium

    Related Posts

    Security

    Nmap 7.96 Launches with Lightning-Fast DNS and 612 Scripts

    May 17, 2025
    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-48187 – RAGFlow Authentication Bypass

    May 17, 2025
    Leave A Reply Cancel Reply

    Hostinger

    Continue Reading

    No, Call of Duty: Black Ops 6 won’t require a massive 300 GB download

    Development

    OWASP Top 10 Vulnerabilities: A Guide for QA Testers

    Development

    Linux Mint: la Migliore Alternativa a Windows 11

    Linux

    Flutter vs React Native for Mobile Apps: What Laravel Devs Say in 2025

    Development

    Highlights

    CVE-2025-22883 – Delta Electronics ISPSoft Out-Of-Bounds Write Vulnerability

    April 30, 2025

    CVE ID : CVE-2025-22883

    Published : April 30, 2025, 8:15 a.m. | 38 minutes ago

    Description : Delta Electronics ISPSoft version 3.20 is vulnerable to an Out-Of-Bounds Write vulnerability that could allow an attacker to execute arbitrary code when parsing DVP file.

    Severity: 7.8 | HIGH

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    ESET APT Activity Report Q2 2024–Q3 2024: Key findings

    November 15, 2024

    Windows 11 Dev KB5058493 adds intelligent text actions in Click to Do for AMD & Intel Copilot + PCs

    May 13, 2025

    CVE-2025-47269 – Code-server Proxy Pathway Token Exfiltration

    May 9, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.