<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Syed Muhammad Haris</title>
    <description>The latest articles on DEV Community by Syed Muhammad Haris (@syed_haris).</description>
    <link>https://dev.to/syed_haris</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3502744%2F4a8d1a6b-b683-45ca-86f8-53d36f1e4a1e.jpeg</url>
      <title>DEV Community: Syed Muhammad Haris</title>
      <link>https://dev.to/syed_haris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syed_haris"/>
    <language>en</language>
    <item>
      <title>Step-by-Step Guide to Setting Up an Angular Application with CMS-Driven Architecture(Optimizely)</title>
      <dc:creator>Syed Muhammad Haris</dc:creator>
      <pubDate>Tue, 23 Sep 2025 13:16:12 +0000</pubDate>
      <link>https://dev.to/syed_haris/-bne</link>
      <guid>https://dev.to/syed_haris/-bne</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8" class="crayons-story__hidden-navigation-link"&gt;Step-by-Step Guide to Setting Up an Angular Application with CMS-Driven Architecture(Optimizely)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/syed_haris" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3502744%2F4a8d1a6b-b683-45ca-86f8-53d36f1e4a1e.jpeg" alt="syed_haris profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/syed_haris" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Syed Muhammad Haris
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Syed Muhammad Haris
                
              
              &lt;div id="story-author-preview-content-2860267" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/syed_haris" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3502744%2F4a8d1a6b-b683-45ca-86f8-53d36f1e4a1e.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Syed Muhammad Haris&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 23 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8" id="article-link-2860267"&gt;
          Step-by-Step Guide to Setting Up an Angular Application with CMS-Driven Architecture(Optimizely)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/angular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;angular&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/optimizely"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;optimizely&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>angular</category>
      <category>optimizely</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Step-by-Step Guide to Setting Up an Angular Application with CMS-Driven Architecture(Optimizely)</title>
      <dc:creator>Syed Muhammad Haris</dc:creator>
      <pubDate>Tue, 23 Sep 2025 07:18:37 +0000</pubDate>
      <link>https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8</link>
      <guid>https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8</guid>
      <description>&lt;p&gt;In this blog, we’ll walk through how our &lt;strong&gt;Angular application&lt;/strong&gt; is architected to work with a &lt;strong&gt;dynamic CMS&lt;/strong&gt; setup using a custom library that acts as the bridge between &lt;strong&gt;Optimizely CMS&lt;/strong&gt; and frontend rendering. We’ll explore the &lt;strong&gt;folder structure&lt;/strong&gt;, &lt;strong&gt;routing flow&lt;/strong&gt;, &lt;strong&gt;component loading&lt;/strong&gt;, and how the &lt;strong&gt;dynamic component&lt;/strong&gt; architecture works behind the scenes.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can access the full source code of this project on GitHub:&lt;br&gt;
&lt;a href="https://github.com/dahik/Optimizely-Angular-Application" rel="noopener noreferrer"&gt;View On GitHub&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Folder Structure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Our Angular application is organized into three main folders:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. DropComponents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains all components that are dropped via CMS into the &lt;strong&gt;PageComponents&lt;/strong&gt; array or the &lt;strong&gt;Blocks&lt;/strong&gt; drop in &lt;strong&gt;CMS Content area&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;These components are dynamically rendered using a &lt;strong&gt;custom directive&lt;/strong&gt; provided by our Angular library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. GlobalComponents&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contains global UI components such as &lt;strong&gt;Header&lt;/strong&gt; and &lt;strong&gt;NotFoundComponent&lt;/strong&gt; (404 page) etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Pages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains route-based page components.&lt;/li&gt;
&lt;li&gt;Includes components that render entire CMS pages such as &lt;strong&gt;PageLayoutComponent&lt;/strong&gt; and detailed content pages like &lt;strong&gt;NewsDetailsComponent&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj5bp3kmnkjkckxvvr7ao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj5bp3kmnkjkckxvvr7ao.png" alt=" " width="301" height="536"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Structure Looks Like:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;src&lt;/strong&gt;&lt;br&gt;
 └──&lt;strong&gt;app&lt;/strong&gt;&lt;br&gt;
      ├──&lt;strong&gt;DropComponents&lt;/strong&gt;&lt;br&gt;
      │    ├── eservice&lt;br&gt;
      │    │     └── eservice.component.ts&lt;br&gt;
      │    └── hero&lt;br&gt;
      │          └── hero.component.ts&lt;br&gt;
      │&lt;br&gt;
      ├──&lt;strong&gt;GlobalComponents&lt;/strong&gt;&lt;br&gt;
      │    ├── header&lt;br&gt;
      │    │     └── header.component.ts&lt;br&gt;
      │    └── not-found&lt;br&gt;
      │          └── not-found.component.ts&lt;br&gt;
      │&lt;br&gt;
      ├──&lt;strong&gt;Pages&lt;/strong&gt;&lt;br&gt;
      │    ├── page-layout&lt;br&gt;
      │    │     └── page-layout.component.ts&lt;br&gt;
      │    └── news-details&lt;br&gt;
      │          └── news-details.component.ts&lt;br&gt;
      │&lt;br&gt;
      └──&lt;strong&gt;app-routing.module.ts&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step By Step Guide:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create a New Angular Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the following command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new demo-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create Project Folders:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;create the following directories or folders inside the &lt;strong&gt;src --&amp;gt; app&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DropComponents&lt;/li&gt;
&lt;li&gt;GlobalComponents&lt;/li&gt;
&lt;li&gt;Pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Generate the Layout Component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside the &lt;strong&gt;Global&lt;/strong&gt; folder, generate a &lt;strong&gt;layoutComponent&lt;/strong&gt; using the Angular CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c Global/layout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Generate the Page Layout Component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside the &lt;strong&gt;Pages&lt;/strong&gt; folder, generate a &lt;strong&gt;pageLayoutComponent&lt;/strong&gt; using the Angular CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c Pages/pageLayout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Now Setup Routing Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our Angular routing is designed such that any route ultimately resolves to the &lt;strong&gt;PageLayoutComponent&lt;/strong&gt;, ensuring all &lt;strong&gt;CMS-driven pages&lt;/strong&gt; go through a common entry point means whatever be the page route the hit will always come to the PageLayoutComponent.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;app-routing.module.ts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
  {
    path: '',
    component: LayoutComponent,
    children: [
      { path: '**', component: PageLayoutComponent } // nested catch-all
    ]
  },

  { path: '**', redirectTo: '' }, // global catch-all
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Response From Backend
&lt;/h2&gt;

&lt;p&gt;we can receive two types of responses from the backend:&lt;/p&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Response with [pageComponents] array&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This array contains all the CMS dropped blocks.&lt;/li&gt;
&lt;li&gt;If it exists in the response, it means the page includes blocks that need to be rendered.&lt;/li&gt;
&lt;li&gt;These blocks are rendered using the library directive &lt;strong&gt;libDynamicComponent&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "data": {
        "Content": {
            "items": [
                {
                    "Type": "StartPage",
                    "LastModifiedDate": "2025-06-03T09:44:34Z",
                    "breadcrumbs": {
                        "Content": {
                            "items": []
                        }
                    },
                    "PageModifiedDateTokenText": "Last Modified Date: {0} Saudi Arabia Time"
                    "BreadcrumbTitle": "Home",
                    "PageComponents": [
                        {
                            "ContentLink": {
                                "Expanded": {
                                    "Type": "HeroBlock",
                                    "OverHeadingText": "Hero",
                                    "Heading": "Hero",
                                    "Description": "Seamless tourism services, available"
                                }
                            }
                        },
                        {
                            "ContentLink": {
                                "Expanded": {
                                    "Type": "EServiceBlock",
                                    "OverHeadingText": "Smart Tourism Services",
                                    "Heading": "E-services",
                                    "Description": "Seamless tourism services, available anytime, anywhere. Access permits, licenses, and essential development tools instantly through our advanced digital platform—designed to streamline and elevate your tourism experience.",
                                    "EServiceCTAUrl": {
                                        "Title": "View all",
                                        "Name": "View all",
                                        "Target": "_self",
                                        "Url": "/en/e-services"
                                    },
                                    "DetailCTAText": "Details",
                                    "StartServiceCTAText": "Start Service"
                                }
                            }
                        }
                    ]
                }
            ]
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ &lt;strong&gt;Response without [pageComponents] array&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In this case, the page is loaded only through its page properties.&lt;/li&gt;
&lt;li&gt;There are no additional components or dropped blocks in the CMS.&lt;/li&gt;
&lt;li&gt;Such pages are rendered using &lt;strong&gt;ViewContainerRef[#dynamicContainer]&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "data": {
        "Content": {
            "items": [
                {
                    "Type": "LeadershipDetailPage",
                    "LastModifiedDate": "2025-05-27T06:18:14Z",
                    "PageModifiedDateTokenText": "Last Modified Date: {0} Saudi Arabia Time",
                    "PageTitle": "About Tourism",
                    "SubTitle": "",
                    "PageShortDescription": ""
                    },
                    "HideSpotlight": false,
                    "HideBreadcrumb": false,
                    "HideShareButton": true,
                    "HidePrintButton": true,
                    "PageImage": {
                        "Expanded": {
                            "Url": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af",
                            "AltText": "",
                            "ProportionsImages": [
                                {
                                    "XXLImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=1400&amp;amp;height=408&amp;amp;rxy=0.5,0.5",
                                    "XLImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=1200&amp;amp;height=350&amp;amp;rxy=0.5,0.5",
                                    "LGImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=992&amp;amp;height=289&amp;amp;rxy=0.5,0.5",
                                    "MDImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=768&amp;amp;height=224&amp;amp;rxy=0.5,0.5",
                                    "SMImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=576&amp;amp;height=168&amp;amp;rxy=0.5,0.5"
                                }
                            ]
                        }
                    },
                    "HideViewBioLink": false,
                    "LeadershipTitle": "Her Highness Princess Haifa Bint Mohammad Al Saud",
                    "LeadershipSubTitle": "Vice Minister of Tourism",
                    "SocialLinkData": [
                        {
                            "Name": "Linked in",
                            "IconCss": "hgi-stroke hgi-linkedin-02",
                            "Url": "https://linkedin.com/"
                        },
                        {
                            "Name": "X",
                            "IconCss": "hgi-stroke hgi-new-twitter",
                            "Url": "https://x.com/"
                        },
                        {
                            "Name": "Facebook",
                            "IconCss": "hgi-stroke hgi-facebook-02",
                            "Url": "https://facebook.com/"
                        },
                        {
                            "Name": "Youtube",
                            "IconCss": "hgi-stroke hgi-youtube",
                            "Url": "https://youtube.com/"
                        }
                    ],
                    "LeadershipPageImage": {
                        "Expanded": {
                            "Url": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1",
                            "AltText": "",
                            "ProportionsImages": [
                                {
                                    "XXLImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=1400&amp;amp;height=408&amp;amp;rxy=0.55,0.46",
                                    "XLImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=1200&amp;amp;height=350&amp;amp;rxy=0.55,0.46",
                                    "LGImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=992&amp;amp;height=289&amp;amp;rxy=0.55,0.46",
                                    "MDImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=768&amp;amp;height=224&amp;amp;rxy=0.55,0.46",
                                    "SMImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=576&amp;amp;height=168&amp;amp;rxy=0.55,0.46"
                                }
                            ]
                        }
                    }
                }
            ]
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a more detailed explanation about responses, you can check out the full blog here: &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8"&gt;Building a Dynamic CMS-Driven Angular App Using Optimizely &amp;amp; a Custom BFF Architecture&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Install Angular Library In our Application
&lt;/h2&gt;

&lt;p&gt;First, paste the &lt;strong&gt;dist&lt;/strong&gt; folder of library into the project’s root directory and rename it to &lt;strong&gt;lib&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oxd95s1r71kh3i7cfpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oxd95s1r71kh3i7cfpa.png" alt=" " width="189" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, Run the below command to install a Library&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; npm install .\lib\optimizely-cms-integration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️Note&lt;/strong&gt;: The library name is &lt;strong&gt;optimizely-cms-integration&lt;/strong&gt;. When you build the library, the &lt;strong&gt;dist&lt;/strong&gt; folder will contain a &lt;strong&gt;subfolder&lt;/strong&gt; with your library’s name—in my case, it is &lt;strong&gt;optimizely-cms-integration&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Component Code and Responsibilities:
&lt;/h2&gt;

&lt;p&gt;1️⃣ In &lt;strong&gt;layout.component.html&lt;/strong&gt; inside the &lt;strong&gt;GlobalComponents&lt;/strong&gt; folder:&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;LayoutComponent&lt;/strong&gt; provides a consistent layout for all pages by rendering a global &lt;strong&gt;&lt;/strong&gt; and a &lt;strong&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;app-header ngSkipHydration&amp;gt;&amp;lt;/app-header&amp;gt;
&amp;lt;div class="content"&amp;gt;
    &amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ Create Drop Components&lt;/p&gt;

&lt;p&gt;In the CMS content area, we now have two dropped components (as shown in the &lt;strong&gt;pageComponents&lt;/strong&gt; array in the response above). Suppose the &lt;strong&gt;Start Page&lt;/strong&gt; consists of these two components, each with its own design.&lt;/p&gt;

&lt;p&gt;To set this up, generate two components inside the &lt;strong&gt;DropComponents&lt;/strong&gt; folder using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c DropComponents/hero
ng g c DropComponents/eservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;hero.component.ts&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Inject, Input, PLATFORM_ID, ViewEncapsulation } from '@angular/core';


@Component({
    selector: 'app-hero',
    templateUrl: './hero.component.html',
    styleUrls: ['./hero.component.css'],
    encapsulation: ViewEncapsulation.None,
    standalone: false
})


export class HeroComponent {
    @Input() data: any; //CMS-response of this Block
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;e-services.component.ts&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Input } from '@angular/core';

@Component({
    selector: 'app-e-services',
    templateUrl: './e-services.component.html',
    styleUrls: ['./e-services.component.css'],
    standalone: false
})

export class EServicesComponent {
  @Input() data: any; //CMS-response of this Block

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️Note&lt;/strong&gt;: The &lt;strong&gt;data&lt;/strong&gt; variable here contains all the content for this component from the &lt;strong&gt;pageComponents&lt;/strong&gt; array. After receiving the data in your component, you can design and structure it as you want.&lt;/p&gt;

&lt;p&gt;3️⃣ Create a Page Component (Without &lt;strong&gt;pageComponents&lt;/strong&gt; Array)&lt;/p&gt;

&lt;p&gt;Next, we’ll create another component for a page type that doesn’t use the &lt;strong&gt;pageComponents&lt;/strong&gt; array and instead depends only on its page properties. For example, a &lt;strong&gt;Leadership Details&lt;/strong&gt; page.&lt;/p&gt;

&lt;p&gt;Run the following command to generate a component inside the &lt;strong&gt;pages&lt;/strong&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c pages/leadership-details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;leadership-details.component.ts&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {Component, Input} from '@angular/core';

@Component({
  selector: 'app-leadership-details',
  standalone: false,
  templateUrl: './leadership-details.component.html',
  styleUrl: './leadership-details.component.css'
})
export class LeadershipDetailsComponent {
  @Input() data: any //CMS-response of this Page

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4️⃣ In &lt;strong&gt;page-layout.component.ts&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we’ll set up the &lt;strong&gt;registries&lt;/strong&gt; that will be used for &lt;strong&gt;CMS pages&lt;/strong&gt; and the &lt;strong&gt;dropped blocks&lt;/strong&gt; within the CMS content area.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//CMS Drop Block Registries:

export const COMP_REGISTRY: { [key: string]: () =&amp;gt; Promise&amp;lt;any&amp;gt; } = {
  HeroBlock: () =&amp;gt;
    import('../../DropComponents/hero/hero.component').then((m) =&amp;gt; m.HeroComponent),
  EServiceBlock: () =&amp;gt;
    import('../../DropComponents/e-services/e-services.component').then((m) =&amp;gt; m.EServicesComponent)
};

//CMS page Registries:

export const PAGE_REGISTRY: { [key: string]: () =&amp;gt; Promise&amp;lt;any&amp;gt; } = {
  ...COMP_REGISTRY,
  LeadershipDetailPage: () =&amp;gt;
    import('../leadership-details/leadership-details.component')
      .then(m =&amp;gt; m.LeadershipDetailsComponent)
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Secondly, register these registries in the library by using the functions:
&lt;strong&gt;setPageRegistry(PAGE_REGISTRY)&lt;/strong&gt; and &lt;strong&gt;setComponentRegistry(COMP_REGISTRY)&lt;/strong&gt;.”
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { OptimizelyCmsIntegrationService } from 'optimizely-cms-integration';


 constructor(
    private dynamicLoader: OptimizelyCmsIntegrationService, private router: Router
  ) {

      // CMS Page registry mapping in library
          this.dynamicLoader.setPageRegistry(PAGE_REGISTRY);

      //CMS Drop Blocks registry mapping in library
          this.dynamicLoader.setComponentRegistry(COMP_REGISTRY);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, we’ll use the library method &lt;strong&gt;getPageContent(baseURL, router.url)&lt;/strong&gt; to fetch the response and pass it to the &lt;strong&gt;renderPages()&lt;/strong&gt; function along with our custom ViewContainerRef. After that, we’ll call the &lt;strong&gt;renderComponents()&lt;/strong&gt; function, which uses the &lt;strong&gt;directive&lt;/strong&gt; in the library as a container to handle the responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;By calling these functions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the response depends entirely on page properties, the library will look into the &lt;strong&gt;Pages&lt;/strong&gt; folder and load the appropriate component from there.&lt;/li&gt;
&lt;li&gt;If the response contains dropped blocks in the CMS content area (via the &lt;strong&gt;pageComponents&lt;/strong&gt; array), those blocks will be rendered accordingly from &lt;strong&gt;DropComponents&lt;/strong&gt; folder.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Router, NavigationEnd } from '@angular/router';
import { Component, ViewChild, OnInit, AfterViewInit, OnDestroy, ViewContainerRef, Injector, NgZone } from '@angular/core';
import { OptimizelyCmsIntegrationService } from 'optimizely-cms-integration';
import { DynamicComponentDirective } from 'optimizely-cms-integration';
import { catchError, map, of, Subscription } from 'rxjs';



//Drop Components Directive 
@ViewChild(DynamicComponentDirective, { static: true }) dynamicHost!: DynamicComponentDirective;

//Page Component ViewContainerRef
@ViewChild('dynamicContainer', { read: ViewContainerRef, static: true })
  dynamicContainer!: ViewContainerRef;

getNewCompletePageData() {

  const baseURL = "your Backend CMS Base URL"

  this.dynamicLoader.getPageContent(baseURL,this.router.url).subscribe({
        next: (data: any) =&amp;gt; {
          if (data) {
            // Render a pages dynamically
            this.dynamicLoader.renderPages(data.Type, data, this.dynamicContainer);

            // Render a component dynamically     
          this.dynamicLoader.renderComponents(this.dynamicHost.viewContainerRef, data);

          }
        }
      });

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Overall Code snippet of "page-layout.component.ts"&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Router, NavigationEnd } from '@angular/router';
import { Component, ViewChild, OnInit, AfterViewInit, OnDestroy, ViewContainerRef, Injector, NgZone } from '@angular/core';
import { OptimizelyCmsIntegrationService } from 'optimizely-cms-integration';
import { DynamicComponentDirective } from 'optimizely-cms-integration';
import { catchError, map, of, Subscription } from 'rxjs';

//CMS Drop Block Registries:

export const COMP_REGISTRY: { [key: string]: () =&amp;gt; Promise&amp;lt;any&amp;gt; } = {
  HeroBlock: () =&amp;gt;
    import('../../DropComponents/hero/hero.component').then((m) =&amp;gt; m.HeroComponent),
  EServiceBlock: () =&amp;gt;
    import('../../DropComponents/e-services/e-services.component').then((m) =&amp;gt; m.EServicesComponent)
};

//CMS page Registries:

export const PAGE_REGISTRY: { [key: string]: () =&amp;gt; Promise&amp;lt;any&amp;gt; } = {
  ...COMP_REGISTRY,
  LeadershipDetailPage: () =&amp;gt;
    import('../leadership-details/leadership-details.component')
      .then(m =&amp;gt; m.LeadershipDetailsComponent)
};

@Component({
  selector: 'app-page-layout',
  templateUrl: './page-layout.component.html',
  styleUrls: ['./page-layout.component.css'],
  standalone: false
})
export class PageLayoutComponent implements OnInit, AfterViewInit, OnDestroy {
//Drop Components Directive 
@ViewChild(DynamicComponentDirective, { static: true }) dynamicHost!: DynamicComponentDirective;

//Page Component ViewContainerRef
@ViewChild('dynamicContainer', { read: ViewContainerRef, static: true })
  dynamicContainer!: ViewContainerRef;


private routeSubscription!: Subscription;

constructor(private dynamicLoader: OptimizelyCmsIntegrationService, private router: Router) 
  {

      // CMS Page registry mapping in library
          this.dynamicLoader.setPageRegistry(PAGE_REGISTRY);

      //CMS Drop Blocks registry mapping in library

       this.dynamicLoader.setComponentRegistry(COMP_REGISTRY);
  }


  ngOnInit() {
  this.routeSubscription = this.router.events.subscribe(event =&amp;gt; {
        if (event instanceof NavigationEnd) {
          this.getNewCompletePageData();
        }
      });

}


getNewCompletePageData() {

  const baseURL = "your Backend CMS Base URL"

  this.dynamicLoader.getPageContent(baseURL,this.router.url).subscribe({
        next: (data: any) =&amp;gt; {
          if (data) {
            // Render a pages dynamically
            this.dynamicLoader.renderPages(data.Type, data, this.dynamicContainer);

            // Render a component dynamically     
          this.dynamicLoader.renderComponents(this.dynamicHost.viewContainerRef, data);

          }
        }
      });

}

  //single layout so router subscription destroty
  ngOnDestroy() {
    if (this.routeSubscription) {
      this.routeSubscription.unsubscribe();
    }
  }


}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5️⃣ In &lt;strong&gt;page-layout.component.html&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;!-- This  dynamicContainer here is responsible to show component which will be load from page properties--&amp;gt;
&amp;lt;ng-container  #dynamicContainer&amp;gt;&amp;lt;/ng-container&amp;gt;


&amp;lt;main className='Page-container'&amp;gt;
   &amp;lt;div&amp;gt;
       &amp;lt;section&amp;gt;
         &amp;lt;!-- This  libDynamicComponent here is responsible to show drop component on page--&amp;gt;
         &amp;lt;ng-template  libDynamicComponent&amp;gt;&amp;lt;/ng-template&amp;gt;
       &amp;lt;/section&amp;gt;
   &amp;lt;/div&amp;gt;
&amp;lt;/main&amp;gt;    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;#dynamicContainer&lt;/strong&gt;: Used for rendering components based on &lt;strong&gt;page Properties&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;libDynamicComponent&lt;/strong&gt;: A library directive responsible for rendering all &lt;strong&gt;DropComponents&lt;/strong&gt; placed on the CMS page. It also supports &lt;strong&gt;dynamic component reordering&lt;/strong&gt;—if a block is reordered or its position is changed in the CMS content area, the update will be reflected in the UI after a hard refresh. On the frontend, you don’t need to manually adjust or replace the order of dropped components; the library handles it automatically.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How the Page Rendering Flow Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User visits a URL → Routed to &lt;strong&gt;PageLayoutComponent&lt;/strong&gt; via &lt;strong&gt;LayoutComponent&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Call &lt;strong&gt;getNewCompletePageData()&lt;/strong&gt; inside &lt;strong&gt;PageLayoutComponent&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Uses library method: &lt;strong&gt;this.dynamicLoader.getPageContent(baseURL, this.router.url)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;This hits &lt;strong&gt;Backend CMS endpoint&lt;/strong&gt; and gets CMS page data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Once data is received:

&lt;ul&gt;
&lt;li&gt;First, the &lt;strong&gt;layout/page&lt;/strong&gt; is rendered using the &lt;strong&gt;pageProperties&lt;/strong&gt; via the library method &lt;strong&gt;renderPages()&lt;/strong&gt;. The data is then passed to the component by checking the &lt;strong&gt;Type&lt;/strong&gt; keyword in backend response and matching it against the &lt;strong&gt;PAGE_REGISTRY&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Next, the &lt;strong&gt;dropped components&lt;/strong&gt; on the CMS page are rendered using the library method &lt;strong&gt;renderComponents()&lt;/strong&gt;. The data is passed to each component by checking the &lt;strong&gt;Type&lt;/strong&gt; keyword in backend response and matching it against the &lt;strong&gt;COMP_REGISTRY&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;⚠️Note&lt;/strong&gt;: This is how you can render all types of pages and blocks on the frontend. Whatever pages or blocks you create in the CMS, you simply need to map them in the registries and design their markup on the frontend. The library methods will then handle passing the data to the correct components automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;By keeping all CMS logic and bindings encapsulated within a reusable Angular library, this architecture ensures a clean separation of concerns. All dynamic rendering decisions are based on CMS response keys (&lt;strong&gt;Type&lt;/strong&gt;) and tied to Angular components via registries.&lt;/p&gt;

&lt;p&gt;This results in a highly dynamic and CMS-author-driven site experience where frontend developers only need to map the component registry and design the components — the rendering is fully automated.&lt;/p&gt;

&lt;p&gt;Refer to a previous blog "&lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8"&gt;Building a Dynamic CMS-Driven Angular App Using Optimizely &amp;amp; a Custom BFF Architecture&lt;/a&gt;" for more on how the BFF integrates and delivers this structure.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Coming Soon&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to Achieve OPE (On-Page Editing) in CMS like Optimizely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow Me&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connect with me on LinkedIn to stay updated with my latest blogs, tutorials, and projects:&lt;br&gt;
&lt;a href="//www.linkedin.com/in/syed-muhammad-haris"&gt;Syed_Muhammad_Haris&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>optimizely</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>SSR Enabling In Angular with Real world Scenario along with the code...</title>
      <dc:creator>Syed Muhammad Haris</dc:creator>
      <pubDate>Tue, 16 Sep 2025 11:40:57 +0000</pubDate>
      <link>https://dev.to/syed_haris/ssr-enabling-in-angular-with-real-world-scenario-along-with-the-code-npe</link>
      <guid>https://dev.to/syed_haris/ssr-enabling-in-angular-with-real-world-scenario-along-with-the-code-npe</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/syed_haris" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3502744%2F4a8d1a6b-b683-45ca-86f8-53d36f1e4a1e.jpeg" alt="syed_haris"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/syed_haris/enabling-ssr-server-side-rendering-in-angular-application-20kg" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;✨ Enabling SSR (Server-Side Rendering) in Angular Application&lt;/h2&gt;
      &lt;h3&gt;Syed Muhammad Haris ・ Sep 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ssr&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#optimizely&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>angular</category>
      <category>ssr</category>
      <category>optimizely</category>
    </item>
    <item>
      <title>Optimizely CMS With Angular In Decoupled Environment</title>
      <dc:creator>Syed Muhammad Haris</dc:creator>
      <pubDate>Tue, 16 Sep 2025 11:39:48 +0000</pubDate>
      <link>https://dev.to/syed_haris/optimizely-cms-with-angular-in-decoupled-environment-3jgp</link>
      <guid>https://dev.to/syed_haris/optimizely-cms-with-angular-in-decoupled-environment-3jgp</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8" class="crayons-story__hidden-navigation-link"&gt;Building a Dynamic CMS-Driven Angular App Using Optimizely &amp;amp; a Custom BFF Architecture&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/syed_haris" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3502744%2F4a8d1a6b-b683-45ca-86f8-53d36f1e4a1e.jpeg" alt="syed_haris profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/syed_haris" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Syed Muhammad Haris
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Syed Muhammad Haris
                
              
              &lt;div id="story-author-preview-content-2846052" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/syed_haris" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3502744%2F4a8d1a6b-b683-45ca-86f8-53d36f1e4a1e.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Syed Muhammad Haris&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 16 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8" id="article-link-2846052"&gt;
          Building a Dynamic CMS-Driven Angular App Using Optimizely &amp;amp; a Custom BFF Architecture
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/angular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;angular&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/optimizely"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;optimizely&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dotnet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dotnet&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>angular</category>
      <category>optimizely</category>
      <category>dotnet</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Enabling SSR (Server-Side Rendering) in Angular Application</title>
      <dc:creator>Syed Muhammad Haris</dc:creator>
      <pubDate>Tue, 16 Sep 2025 11:31:41 +0000</pubDate>
      <link>https://dev.to/syed_haris/enabling-ssr-server-side-rendering-in-angular-application-20kg</link>
      <guid>https://dev.to/syed_haris/enabling-ssr-server-side-rendering-in-angular-application-20kg</guid>
      <description>&lt;p&gt;In this blog post, we’ll walk through how to enable &lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt; in an Angular application. We’ll explore a real-world scenario and see step by step how SSR helps us solve it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Add SSR?
&lt;/h2&gt;

&lt;p&gt;Angular typically renders content in the browser, which isn’t optimal for search engines or initial load performance. SSR enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search engine indexing of dynamic content&lt;/li&gt;
&lt;li&gt;Fast first-paint and improved Time-to-Interactive (TTI)&lt;/li&gt;
&lt;li&gt;Custom HTTP status codes like 404&lt;/li&gt;
&lt;li&gt;Preview rendering for CMS editors&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Scenario: Handling Custom HTTP Status Codes (e.g., 404)
&lt;/h2&gt;

&lt;p&gt;Imagine you have different pages in your Angular app that need to return specific &lt;strong&gt;HTTP status codes&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example&lt;/strong&gt;, when a page is &lt;strong&gt;not found&lt;/strong&gt;, you want the server to send back a proper ** 404** status code.&lt;/p&gt;

&lt;p&gt;Normally, Angular applications running in the browser cannot send &lt;strong&gt;HTTP status codes&lt;/strong&gt;. However, with &lt;strong&gt;SSR&lt;/strong&gt;, we can inject the server’s &lt;strong&gt;response object&lt;/strong&gt; and return custom status codes like &lt;strong&gt;404 (Not Found)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Library Used
&lt;/h2&gt;

&lt;p&gt;We use &lt;strong&gt;@nguniversal/express-engine&lt;/strong&gt;, which provides an Express-based server adapter to render Angular on the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step-by-Step Setup for SSR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Add Angular Universal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to add Angular Universal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @nguniversal/express-engine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Verify File Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the command, ensure the following files exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;server.ts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;main.ts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;main.server.ts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Updated &lt;strong&gt;angular.json&lt;/strong&gt; with server target&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  File Updates and Configuration
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;server.ts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure the &lt;strong&gt;server.ts&lt;/strong&gt; file looks like this to support client-side routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine, isMainModule } from '@angular/ssr/node';
import express from 'express';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import AppServerModule from './main.server';
import { RESPONSE } from '@nguniversal/express-engine/tokens';
import https from 'https';
import fs from 'fs';

const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');

const httpsOptions = {
  key: fs.readFileSync('./ssl-certificates/ssl.key'),
  cert: fs.readFileSync('./ssl-certificates/ssl.crt')
};

const app = express();
const commonEngine = new CommonEngine();

app.get(
  '**',
  express.static(browserDistFolder, {
    maxAge: '1y',
    index: 'index.html'
  }),
);

app.get('**', (req, res, next) =&amp;gt; {
  const { protocol, originalUrl, baseUrl, headers } = req;

  commonEngine
    .render({
      bootstrap: AppServerModule,
      documentFilePath: indexHtml,
      url: `${protocol}://${headers.host}${originalUrl}`,
      publicPath: browserDistFolder,
      providers: [
        { provide: APP_BASE_HREF, useValue: baseUrl },
        { provide: RESPONSE, useValue: res }
      ],
    })
    .then((html) =&amp;gt; res.send(html))
    .catch((err) =&amp;gt; next(err));
});

if (isMainModule(import.meta.url)) {
  const port = process.env['PORT'] || 4200;
  https.createServer(httpsOptions, app).listen(port, () =&amp;gt; {
    console.log(`🚀 Angular SSR server running at https://localhost:${port}`);
  });
}

export default app;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;main.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;reference types="@angular/localize" /&amp;gt;
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err =&amp;gt; console.error(err));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;main.server.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export { AppServerModule as default } from './app/app.module.server';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️Note: By updating the code in the file mentioned above, you can enable SSR in your Angular application.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 How to Run SSR ?
&lt;/h2&gt;

&lt;p&gt;You need to add a server script in &lt;strong&gt;package.json&lt;/strong&gt;. In some cases, it’s added by default, but if not, you’ll need to add it manually as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build",
  "watch": "ng build --watch --configuration development",
  "test": "ng test",
  "serve:ssr:Demo_App": "node dist/demo-app/server/server.mjs"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ Replace &lt;em&gt;"Demo_App"&lt;/em&gt; and &lt;em&gt;"demo-app"&lt;/em&gt; with your actual project name.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then, Run this command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run serve:ssr:Demo_App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real-World Use Case
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SSR 404 Page Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we will change the Status code of our specific component which will be render when ever we get the response code 404 then it will update the status code of that page to 404.&lt;/p&gt;

&lt;p&gt;To handle &lt;strong&gt;404&lt;/strong&gt; responses in our Angular &lt;strong&gt;SSR&lt;/strong&gt; app, we’ll follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a 404 Page Component&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a dedicated component that will be rendered whenever the backend returns a 404 response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Use the Page Registry&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With the help of the page registry(&lt;strong&gt;PAGE_REGISTRY&lt;/strong&gt;) we created in the previous blog (Heading: "&lt;strong&gt;Component Registries (The Bridge)&lt;/strong&gt;") &lt;a href="https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8"&gt;Building a Dynamic CMS-Driven Angular App Using Optimizely &amp;amp; a Custom BFF Architecture&lt;/a&gt;, our library can detect when the response contains a &lt;strong&gt;404 status&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Once detected, it will automatically map and route the &lt;strong&gt;response&lt;/strong&gt; to the &lt;strong&gt;new 404 component&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Render the 404 Page Component&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the &lt;strong&gt;404 component&lt;/strong&gt; is triggered, it receives the &lt;strong&gt;response&lt;/strong&gt; data.&lt;/li&gt;
&lt;li&gt;At this point, we replace the existing logic with the code shown below to update the &lt;strong&gt;HTTP status code&lt;/strong&gt; to &lt;strong&gt;404&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { RESPONSE } from '@nguniversal/express-engine/tokens';

constructor(
  private router: Router,
  @Optional() @Inject(RESPONSE) private response: any
) {}

ngOnInit(): void {
  if (this.response) {
    this.response.statusCode = 404;
    this.response.statusMessage = 'Page Not Found';
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ Note: Using the above code, you can easily update the status code of any page or component in Angular.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Architecture Note&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This flow is fully compatible with the &lt;strong&gt;BFF (Backend for Frontend)&lt;/strong&gt; architecture I’m using.&lt;/li&gt;
&lt;li&gt;However, you can also customize and update &lt;strong&gt;HTTP status codes&lt;/strong&gt; in your own project based on different conditions or requirements.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Enabling &lt;strong&gt;SSR&lt;/strong&gt; in Angular using &lt;strong&gt;Angular Universal&lt;/strong&gt; gives you a huge &lt;strong&gt;SEO&lt;/strong&gt; and &lt;strong&gt;performance boost&lt;/strong&gt; while keeping all the client-side flexibility of Angular.&lt;/p&gt;

&lt;p&gt;Use this setup to build production-ready, CMS-driven, and search engine–friendly Angular applications!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Follow Me&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connect with me on LinkedIn to stay updated with my latest blogs, tutorials, and projects:&lt;br&gt;
&lt;a href="//www.linkedin.com/in/syed-muhammad-haris"&gt;Syed_Muhammad_Haris&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>ssr</category>
      <category>optimizely</category>
    </item>
    <item>
      <title>Building a Dynamic CMS-Driven Angular App Using Optimizely &amp; a Custom BFF Architecture</title>
      <dc:creator>Syed Muhammad Haris</dc:creator>
      <pubDate>Tue, 16 Sep 2025 06:32:19 +0000</pubDate>
      <link>https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8</link>
      <guid>https://dev.to/syed_haris/building-a-dynamic-cms-driven-angular-app-using-optimizely-a-custom-bff-architecture-5ac8</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the era of content-rich web experiences, managing complex frontend logic with decoupled CMS data can be challenging.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore a robust &lt;strong&gt;Back-End for Front-End (BFF)&lt;/strong&gt; pattern using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;Optimizely Content Graph&lt;/li&gt;
&lt;li&gt;A custom Angular library for dynamic component rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️&lt;strong&gt;Note: We've built custom backend APIs that interact with the Optimizely Content Graph and return frontend-friendly responses.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works – End-to-End Flow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When a user visits any CMS page:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user navigates to a route in the Angular app.&lt;/li&gt;
&lt;li&gt;PageLayoutComponent is triggered — the shell component for rendering.&lt;/li&gt;
&lt;li&gt;It calls a method from our Angular library, which:

&lt;ul&gt;
&lt;li&gt;Hits our custom backend API.&lt;/li&gt;
&lt;li&gt;That in turn queries the Content Graph.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The API response includes:

&lt;ul&gt;
&lt;li&gt;Page metadata (title, subtitle, etc.)&lt;/li&gt;
&lt;li&gt;Page Type&lt;/li&gt;
&lt;li&gt;Drop components (Hero, Tabs, Cards, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The library:

&lt;ul&gt;
&lt;li&gt;Determines if the page is rendered entirely via its type&lt;/li&gt;
&lt;li&gt;Or composed from drop components&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Architecture at a Glance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimizely CMS&lt;/strong&gt;: Content authors manage content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Backend APIs&lt;/strong&gt;: Fetch from Content Graph and shape data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angular Library&lt;/strong&gt;: Dynamically renders components based on CMS      type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PageLayoutComponent&lt;/strong&gt;: The main entry point for CMS-driven routes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Happens When a User Visits a Page?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Let’s walk through the interaction step-by-step:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User navigates to &lt;strong&gt;/leadership/john-doe&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Angular loads &lt;strong&gt;PageLayoutComponent&lt;/strong&gt; via routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;getPageContent()&lt;/strong&gt; is called with the current URL&lt;/li&gt;
&lt;li&gt;Backend returns:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Type": "LeadershipDetailPage",
  "PageComponents": [
    { "Type": "HeroBlock", ... }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Angular library&lt;/strong&gt;:

&lt;ol&gt;
&lt;li&gt;Loads &lt;strong&gt;LeadershipDetailPage&lt;/strong&gt; from the Page Registry&lt;/li&gt;
&lt;li&gt;Injects it into the layout container&lt;/li&gt;
&lt;li&gt;If drop components exist:

&lt;ul&gt;
&lt;li&gt;Injects them using the &lt;strong&gt;Component Registry&lt;/strong&gt; via a directive.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This approach supports both template-driven pages and CMS-composed pages using a single layout.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens When a Page Loads
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In my Angular app, the router is configured to redirect all CMS paths or routes to the &lt;strong&gt;PageLayoutComponent&lt;/strong&gt;, as shown in the routing setup below:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;strong&gt;app-routing.module.ts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
  {
    path: '',
    component: LayoutComponent,
    children: [
      { path: '**', component: PageLayoutComponent } // nested catch-all
    ]
  },

  { path: '**', redirectTo: '' }, // global catch-all
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It calls:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;getPageContent(baseURL, router.url)&lt;/strong&gt; --&amp;gt; This library method calls the backend and retrieves the page response for the specified route. &lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;page-layout.component.ts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngOnInit(){
    const baseURL = typeof window !== 'undefined' ? (window as any).env?.apiUrl : '';

    this.dynamicLoader.getPageContent(baseURL,this.router.url).subscribe({
      next: (data: any) =&amp;gt; {
        if (data) {
          console.log('data',data);
        }
      }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The backend returns:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;PageType&lt;/strong&gt;: to determine which full-page component to load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pageComponents&lt;/strong&gt;: drop components like Hero, Cards, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The response is shown below:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
{
    "data": {
        "Content": {
            "items": [
                {
                    "Type": "StartPage",
                    "LastModifiedDate": "2025-06-03T09:44:34Z",
                    "breadcrumbs": {
                        "Content": {
                            "items": []
                        }
                    },
                    "PageModifiedDateTokenText": "Last Modified Date: {0} Saudi Arabia Time"
                    "BreadcrumbTitle": "Home",
                    "PageComponents": [
                        {
                            "ContentLink": {
                                "Expanded": {
                                    "Type": "HeroBlock",
                                    "OverHeadingText": "Hero",
                                    "Heading": "Hero",
                                    "Description": "Seamless tourism services, available"
                                }
                            }
                        },
                        {
                            "ContentLink": {
                                "Expanded": {
                                    "Type": "EServiceBlock",
                                    "OverHeadingText": "Smart Tourism Services",
                                    "Heading": "E-services",
                                    "Description": "Seamless tourism services, available anytime, anywhere. Access permits, licenses, and essential development tools instantly through our advanced digital platform—designed to streamline and elevate your tourism experience.",
                                    "EServiceCTAUrl": {
                                        "Title": "View all",
                                        "Name": "View all",
                                        "Target": "_self",
                                        "Url": "/en/e-services"
                                    },
                                    "DetailCTAText": "Details",
                                    "StartServiceCTAText": "Start Service"
                                }
                            }
                        }
                    ]
                }
            ]
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Understanding Page Types
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pages Rendered from Page Properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some pages, such as &lt;strong&gt;LeadershipDetailPage&lt;/strong&gt;, are rendered entirely based on their CMS type. This means that when a user visits the URL, these pages don’t include any drop components in the CMS content area means in the response their is no &lt;strong&gt;pageComponents[]&lt;/strong&gt; array. Instead, all of their details are contained within the &lt;strong&gt;items[]&lt;/strong&gt; array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response looks like:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
{
    "data": {
        "Content": {
            "items": [
                {
                    "Type": "LeadershipDetailPage",
                    "LastModifiedDate": "2025-05-27T06:18:14Z",
                    "PageModifiedDateTokenText": "Last Modified Date: {0} Saudi Arabia Time",
                    "PageTitle": "About Tourism",
                    "SubTitle": "",
                    "PageShortDescription": ""
                    },
                    "HideSpotlight": false,
                    "HideBreadcrumb": false,
                    "HideShareButton": true,
                    "HidePrintButton": true,
                    "PageImage": {
                        "Expanded": {
                            "Url": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af",
                            "AltText": "",
                            "ProportionsImages": [
                                {
                                    "XXLImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=1400&amp;amp;height=408&amp;amp;rxy=0.5,0.5",
                                    "XLImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=1200&amp;amp;height=350&amp;amp;rxy=0.5,0.5",
                                    "LGImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=992&amp;amp;height=289&amp;amp;rxy=0.5,0.5",
                                    "MDImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=768&amp;amp;height=224&amp;amp;rxy=0.5,0.5",
                                    "SMImage": "https://cms-url/siteassets/about-us/leadership/leaadership_banner.jpg?code=49b3af&amp;amp;width=576&amp;amp;height=168&amp;amp;rxy=0.5,0.5"
                                }
                            ]
                        }
                    },
                    "HideViewBioLink": false,
                    "LeadershipTitle": "Her Highness Princess Haifa Bint Mohammad Al Saud",
                    "LeadershipSubTitle": "Vice Minister of Tourism",
                    "SocialLinkData": [
                        {
                            "Name": "Linked in",
                            "IconCss": "hgi-stroke hgi-linkedin-02",
                            "Url": "https://linkedin.com/"
                        },
                        {
                            "Name": "X",
                            "IconCss": "hgi-stroke hgi-new-twitter",
                            "Url": "https://x.com/"
                        },
                        {
                            "Name": "Facebook",
                            "IconCss": "hgi-stroke hgi-facebook-02",
                            "Url": "https://facebook.com/"
                        },
                        {
                            "Name": "Youtube",
                            "IconCss": "hgi-stroke hgi-youtube",
                            "Url": "https://youtube.com/"
                        }
                    ],
                    "LeadershipPageImage": {
                        "Expanded": {
                            "Url": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1",
                            "AltText": "",
                            "ProportionsImages": [
                                {
                                    "XXLImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=1400&amp;amp;height=408&amp;amp;rxy=0.55,0.46",
                                    "XLImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=1200&amp;amp;height=350&amp;amp;rxy=0.55,0.46",
                                    "LGImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=992&amp;amp;height=289&amp;amp;rxy=0.55,0.46",
                                    "MDImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=768&amp;amp;height=224&amp;amp;rxy=0.55,0.46",
                                    "SMImage": "https://cms-url/contentassets/91bc22483f704dc6a2e244d247a44a0a/haifa-bint-mohammad-al-saud.jpg?code=49bcf1&amp;amp;width=576&amp;amp;height=168&amp;amp;rxy=0.55,0.46"
                                }
                            ]
                        }
                    }
                }
            ]
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pages with Drop Components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Other pages, such as &lt;strong&gt;Home or StartPage&lt;/strong&gt;, rely on dropped blocks in the CMS editor. This means the page details are defined through specific blocks in the CMS, which are then rendered as different components on the frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HeroBlock&lt;/li&gt;
&lt;li&gt;EServiceBlock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the backend response, you can determine that there are two types of pages. The first type loads directly, meaning the page is fully dependent on its page properties, and the response should be passed to that specific component. The second type includes dropped components, where each part of the response is mapped to its corresponding Angular component—for example, a &lt;strong&gt;HeroBlock&lt;/strong&gt; response maps to the &lt;strong&gt;HeroComponent&lt;/strong&gt;, and an &lt;strong&gt;EServiceBlock&lt;/strong&gt; response maps to the &lt;strong&gt;EServiceComponent&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The response here is the same as the one shown above under heading "What Happens When a Page Loads" point no. 3.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Component Registries (The Bridge)
&lt;/h2&gt;

&lt;p&gt;To implement this technique, you need a mapping mechanism that links the &lt;strong&gt;Type&lt;/strong&gt; coming in the response to the component that should be rendered.&lt;/p&gt;

&lt;p&gt;Next, we’ll create two &lt;strong&gt;registries&lt;/strong&gt; in our Angular app to define which Angular component should be loaded based on the &lt;strong&gt;Type&lt;/strong&gt; received in the response. The first registry will handle pages that are rendered entirely from page properties, while the second will handle pages composed of dropped components(&lt;strong&gt;pageComponents[]&lt;/strong&gt; array in response).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drop Component Registry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This registry ensures that the correct Angular component is rendered for each &lt;strong&gt;Type&lt;/strong&gt; found in the &lt;strong&gt;pageComponents[]&lt;/strong&gt; array (dropped components from the CMS).&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;page-layout.component.ts&lt;/strong&gt; [above ngOnInit()]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const COMP_REGISTRY = {
  HeroBlock: () =&amp;gt; import('../../DropComponents/hero/hero.component')
.then(m =&amp;gt; m.HeroComponent),
  EServiceBlock: () =&amp;gt; import('../../DropComponents/eservice/eservice.component')
.then(m =&amp;gt; m.EServiceComponent),
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Page Registry (Pages directly load from properties)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This registry ensures that the correct Angular component is rendered for &lt;strong&gt;Type&lt;/strong&gt; found in the &lt;strong&gt;items[]&lt;/strong&gt; array in the main response.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;page-layout.component.ts&lt;/strong&gt; [above ngOnInit()]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const PAGE_REGISTRY = {
  NewsDetailPage: () =&amp;gt; import('../news-details/news-details.component')
.then(m =&amp;gt; m.NewsDetailsComponent),
 ErrorPage404Block: () =&amp;gt;
    import('../../GlobalComponents/not-found/not-found.component').then((m) =&amp;gt; m.NotFoundComponent),
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After retrieving the response from &lt;strong&gt;getPageContent()&lt;/strong&gt;, we register the two registries in the library using the methods shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Properties render page registry mapping in library
 this.dynamicLoader.setPageRegistry(PAGE_REGISTRY);

//Drop component registry mapping in library
 this.dynamicLoader.setComponentRegistry(COMP_REGISTRY);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library exposes &lt;strong&gt;setPageRegistry()&lt;/strong&gt; and &lt;strong&gt;setComponentRegistry()&lt;/strong&gt; so the main app provides mappings.&lt;/p&gt;

&lt;p&gt;In the future, our Angular library directive will use these registries to pass data to the appropriate components—whether it’s a direct page which renders from properties or dropped components within a page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dynamic Rendering Based on ViewContainerRef
&lt;/h2&gt;

&lt;p&gt;For pages rendered directly from page properties, use a &lt;strong&gt;@ViewChild&lt;/strong&gt; decorator to access the &lt;strong&gt;ViewContainerRef&lt;/strong&gt;. This allows the Angular library to dynamically inject the appropriate components into it.&lt;/p&gt;

&lt;p&gt;Once the container is initialized, we pass the &lt;strong&gt;data&lt;/strong&gt;, its &lt;strong&gt;Type&lt;/strong&gt;, and the &lt;strong&gt;containerRef&lt;/strong&gt; to the library method &lt;strong&gt;renderPages(data.Type, data, this.dynamicContainer)&lt;/strong&gt;. The &lt;strong&gt;renderPages(type, response, containerRef)&lt;/strong&gt; method then looks up the &lt;strong&gt;registry (PAGE_REGISTRY)&lt;/strong&gt; to match the Type, injects the corresponding Angular component into the container, and provides it with the relevant data from the backend response.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;page-layout.component.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ViewChild('dynamicContainer', { read: ViewContainerRef, static: true })
dynamicContainer!: ViewContainerRef;

ngOnInit(){
    const baseURL = typeof window !== 'undefined' ? (window as any).env?.apiUrl : '';

    this.dynamicLoader.getPageContent(baseURL,this.router.url).subscribe({
      next: (data: any) =&amp;gt; {
        if (data) {

          // Render a pages dynamically which is depend on page properties
          this.dynamicLoader.renderPages(data.Type, data, this.dynamicContainer);
        }
      }
    });
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;page-layout.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-container  #dynamicContainer&amp;gt;&amp;lt;/ng-container&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Directive-Based Dynamic Rendering
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;custom directive&lt;/strong&gt; that exposes a ViewContainerRef.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;@ViewChild&lt;/strong&gt; to get a reference to that directive in your   component.&lt;/li&gt;
&lt;li&gt;Pass the directive’s viewContainerRef along with the backend data to the &lt;strong&gt;renderComponents()&lt;/strong&gt; method.&lt;/li&gt;
&lt;li&gt;The library checks the &lt;strong&gt;COMP_REGISTRY&lt;/strong&gt; to find which Angular component matches the Type from the response.&lt;/li&gt;
&lt;li&gt;The matched component is then injected into the container and receives the data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In  &lt;strong&gt;page-layout.component.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ViewChild(DynamicComponentDirective, { static: true }) dynamicHost!: DynamicComponentDirective;

ngOnInit(){
    const baseURL = typeof window !== 'undefined' ? (window as any).env?.apiUrl : '';

    this.dynamicLoader.getPageContent(baseURL,this.router.url).subscribe({
      next: (data: any) =&amp;gt; {
        if (data) {

           // Render a component dynamically
    this.dynamicLoader.renderComponents(this.dynamicHost.viewContainerRef, data);
        }
      }
    });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;page-layout.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;ng-template  libDynamicComponent&amp;gt;&amp;lt;/ng-template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Behind the Scenes:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Components are extracted from &lt;strong&gt;PageComponents&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each block is matched by its &lt;strong&gt;Type (e.g., HeroBlock)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The library uses the component registry to lazy-load and inject the corresponding Angular component.&lt;/li&gt;
&lt;li&gt;CMS data is passed into the component via &lt;strong&gt;@Input()&lt;/strong&gt; bindings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;This directive approach allows editors to add, remove, or reorder blocks in the CMS — and the Angular app reflects changes automatically at runtime.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-Time SSR Adaptation (Server-Side Rendering with Angular Universal)
&lt;/h2&gt;

&lt;p&gt;To make this system SEO-friendly and search engine discoverable, we use Angular Universal for server-side rendering (SSR).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Add SSR?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Angular renders content in the browser by default, SSR enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search engine indexing of dynamic content&lt;/li&gt;
&lt;li&gt;Fast first-paint and Time-to-Interactive (TTI)&lt;/li&gt;
&lt;li&gt;Custom HTTP status codes (like 404s)&lt;/li&gt;
&lt;li&gt;Preview rendering for CMS editors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔧 Library Used: @nguniversal/express-engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Angular Universal provides an Express-based server adapter. We use it to inject HTTP-level logic inside Angular components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step Setup for SSR&lt;/strong&gt;: &lt;a href="https://dev.to/syed_haris/enabling-ssr-server-side-rendering-in-angular-application-20kg"&gt;Enabling SSR (Server-Side Rendering) in Angular Application&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  SSR + Dynamic CMS = Best of Both Worlds
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;With Angular Universal + Dynamic Component Directive&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your content loads instantly on first hit&lt;/li&gt;
&lt;li&gt;CMS changes reflect in real time&lt;/li&gt;
&lt;li&gt;Proper HTTP responses are delivered from the server&lt;/li&gt;
&lt;li&gt;Search engines can fully crawl and understand your content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This makes your Angular frontend fully compatible with modern CMS needs while staying SEO-compliant and dynamic.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Pattern Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt;&lt;br&gt;
Angular handles presentation, while APIs handle data shaping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;br&gt;
Editors can build structured or composable pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;br&gt;
New components? Just update the registry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSR Ready&lt;/strong&gt;&lt;br&gt;
This works with Angular Universal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;br&gt;
Drop components are modular and lazy-loaded.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Benefits
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CMS Flexibility&lt;/strong&gt;: Build pages freely with or without blocks&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic Rendering&lt;/strong&gt;: Powered by simple backend mappings&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: Registries make it easy to plug and play&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Components load only when needed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSR Compatibility&lt;/strong&gt;: Angular Universal plays nicely with dynamic rendering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;By combining&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular’s dynamic loading&lt;/li&gt;
&lt;li&gt;A smart, registry-based Angular library&lt;/li&gt;
&lt;li&gt;A custom BFF layer&lt;/li&gt;
&lt;li&gt;And Optimizely CMS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…you get a scalable, maintainable, &lt;strong&gt;CMS-flexible frontend architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whether rendering full-page types or dynamic blocks — this system ensures your content is powerful, flexible, and consistent.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related Articles&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/syed_haris/step-by-step-guide-to-setting-up-an-angular-application-with-cms-driven-architectureoptimizely-1ka8"&gt;Step-by-Step Guide to Setting Up an Angular Application with CMS-Driven Architecture(Optimizely)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/syed_haris/enabling-ssr-server-side-rendering-in-angular-application-20kg"&gt;Enabling SSR (Server-Side Rendering) in Angular Application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Coming Soon&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to Achieve OPE (On-Page Editing) in CMS like Optimizely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow Me&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connect with me on LinkedIn to stay updated with my latest blogs, tutorials, and projects:&lt;br&gt;
&lt;a href="//www.linkedin.com/in/syed-muhammad-haris"&gt;Syed_Muhammad_Haris&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>optimizely</category>
      <category>dotnet</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
