---
title: "Working with placeholders in a JSS Angular app"
slug: "working-with-placeholders-in-a-jss-angular-app"
abstract: "How placeholder components work in JSS Angular applications and techniques for using them"
---
# Working with placeholders in a JSS Angular app

[Placeholders](https://doc.sitecore.com/xp/en/developers/hd/23/sitecore-headless-development/introducing-placeholders-in-jss-apps.html "Introducing placeholders in JSS apps") are vital components in any application built with Sitecore JSS. To provide examples, this topic uses the Sitecore JSS Angular sample application.

The JSS Angular sample application comes with a root placeholder. The root placeholder is defined in `src/app/routing/layout/layout.component.html`.

The `sc-placeholder` component is imported from Sitecore JSS for Angular, `@sitecorelabs/sitecore-jss-angular`.

```text
<sc-placeholder name="jss-main" [rendering]="route" (loaded)="onPlaceholderLoaded($event)"></sc-placeholder><sc-placeholder name="jss-main" [rendering]="route"></sc-placeholder>

```

When using Angular 20 that uses standalone components by default, you must explicitly import the placeholder in your `@Component` definition as shown:

```ts
import { PlaceholderComponent } from '@sitecore-jss/sitecore-jss/angular';
....
@Component {
  ...
  imports: [PlaceholderComponent],
}

```

The `sc-placeholder` Angular component has a `name` attribute with the value `jss-main`, indicating that it represents the Sitecore placeholder named `jss-main`. The placeholder renders one or more components based on route data, as indicated by the data bound to the `[rendering]` input of the placeholder component:

```text
<sc-placeholder name="jss-main" [rendering]="route" (loaded)="onPlaceholderLoaded($event)"></sc-placeholder>

```

> [!note]
> You can apply `sc-placeholder` to an existing DOM element as an attribute.

<details>
<summary><h2>Understanding the origin of route data</h2></summary>

To understand how the placeholder component receives the `route` data, you must understand how the various components, application modules, and services work together.

In production, the script for building the JSS Angular application builds a server bundle and a client bundle to support [universal rendering](https://angular.io/guide/universal). After running the script `jss build`, you can find the bundles at the following locations:

- Client bundle - located in `dist/browser/`.
- Server bundle - located at `dist/server.bundle.js`.

The two bundles cannot run independently. They must be served by a server. JSS comes with a JavaScript view engine for Node that can run the server bundle. The JSS JavaScript view engine expects a *server bundle* that exports a `renderView` function.

To build and serve the two bundles, the JSS Angular app defines two entry points for the application, with associated app modules and data services.

The file `src/main.server.ts` defines the entry point for the server bundle, bootstrapping the application module for the server `AppServerModule`, defined in `src/app/app.server.module.ts`. The `AppServerModule` uses the `JssContextServerSideService`, defined in `src/app/jss-context.server-side.service.ts`, that runs on the server and stores the context data for the JSS app. The context data contains the data for the current route and Sitecore context data.

The file `src/main.ts` defines the entry point for the client bundle, bootstrapping the application module for client rendering `AppModule`, defined in `src/app/app.module.ts`. The `AppModule` uses the `JssContextService`, defined in `src/app/jss-context.service.ts`, that runs on the client (in the browser) and stores the context data for the JSS app.

When the application runs in the browser from the server bundle, the server invokes the `renderView` function that renders the `AppServerModule`, preparing the context data for the JSS app. For implementation details, see the file `server.bundle.ts` and associated services. Both context services use the Angular `TransferState` class. The class is a key-value store that is transferred from the application module on the server-side to the application module on the client-side, after normalizing the data:

```ts
renderModule(AppServerModule, {
      document: template,
      url: path,
      extraProviders: [
        // custom injection with the initial state that SSR should utilize
        { provide: 'JSS_SERVER_LAYOUT_DATA', useValue: transferState },
        { provide: 'JSS_SERVER_VIEWBAG', useValue: state.viewBag },
      ],
    })
      .then((html) => callback(null, { html }))
      .catch((err) => callback(err, null));

```

The client-side `AppModule` bootstraps the root application component `AppComponent`, defined in `src/app/app.component.ts` , and imports the `RoutingModule` class, defined in the file `src/app/routing/routing.module.ts`. The `RoutingModule` is responsible for attaching the Angular router to the application root, allowing the JSS Angular app to use an Angular router outlet.

The `RoutingModule` defines which component renders in the router outlet placeholder. This component is `LayoutComponent`, defined in the `src/app/routing/layout/layout.component.ts` file. The `RoutingModule` uses the `JssRouteResolver` provider that, when resolving a route, sets the state for the active route.

Finally, the `LayoutComponent`, injected with both `route` and layout data, can subscribe to the active route and extract the route data from the Sitecore context.

At this point, the placeholder component can render the HTML markup for the route in a production setting.

If you work in [disconnected mode](https://doc.sitecore.com/xp/en/developers/hd/23/sitecore-headless-development/start-a-jss-app-in-disconnected-mode.html "Start a JSS app in disconnected mode"), the JSS application retrieves route data from locally defined files. The sample application includes some pre-defined local data in the folder `src/data`. When loading a route, for example, the home page, the application fetches data from the file `src/data/en.yml`. The response contains the JSON data for the route:

```json
{
  "context": {
    "pageEditing": false,
    "site": {
      "name": "JssDisconnectedLayoutService"
    },
    "pageState": "normal",
    "language": "en"
  },
  "route": {
    "databaseName": "available-in-connected-mode",
    "deviceId": "available-in-connected-mode",
    "itemId": "home-page",
    "itemLanguage": "en",
    "itemVersion": 1,
    "layoutId": "available-in-connected-mode",
    "templateId": "available-in-connected-mode",
    "templateName": "available-in-connected-mode",
    "name": "home",
    "fields": {
      "pageTitle": {
        "value": "Welcome to Sitecore JSS"
      }
    },
    "placeholders": {
      "jss-main": [
        {
          "uid": "{2C4A53CC-9DA8-5F51-9D79-6EE2FC671B2D}",
          "componentName": "ContentBlock",
          "dataSource": "available-in-connected-mode",
          "params": {},
          "fields": {
            "heading": {
              "value": "Welcome to Sitecore JSS"
            },
            "content": {
              "value": "<p>Thanks for using JSS. Here are some resources to get you started:</p>"
            }
          }
        }
      ]
    }
  }}

```

The JSON data structure, in the `route` property, contains a list of `placeholders`. Each placeholder name is associated with an array of components. We call the mapping of components to placeholders *component placement rules*.

Because the `sc-placeholder` component has access to the `placeholders` object within the `route` property, it finds the matching placeholder by name (`jss-main`) and renders the array of components within it. In this example, it renders the `ContentBlockComponent` component, a regular Angular component, dynamically and provides the `heading` and `content`, fields through a dependency injection token:

```ts
import { Component, Input } from '@angular/core';
import { ComponentRendering } from '@sitecore-jss/sitecore-jss-angular';
@Component({
  selector: 'app-content-block',
  templateUrl: './content-block.component.html',
  imports: [ComponentRendering, Input] // For Angular 20
})
export class ContentBlockComponent {
  @Input() rendering: ComponentRendering;
}

```

The `rendering` property includes all the information regarding the `ContentBlockComponent` component coming from the data:

```csharp
"componentName": "ContentBlock",
    "dataSource": "available-in-connected-mode",
    "params": {},
    "fields": {
        "heading": {
            "value": "Welcome to Sitecore JSS"
        },
    "content": {
        "value": "<p>Thanks for using JSS. Here are some resources to get you started:</p>"
    }
}

```

</details>

<details>
<summary><h2>Nesting placeholders and components</h2></summary>

Because components can contain their own `sc-placeholder` components, you can define their contents with `placeholders` in the component placement rules.

For example, the `WelcomeComponent` component might contain the following:

```ts
import { PlaceholderComponent } from '@sitecore-jss/sitecore-jss/angular';
....
@Component({
  selector: 'app-welcome',
  template: `
    <sc-placeholder name="welcome" [rendering]="rendering"></sc-placeholder>
  `,
  imports: [PlaceholderComponent] // For Angular 20
})
export class WelcomeComponent {
  rendering: any;
}

```

In this case, it then defines its child components in its component placement rules:

```csharp
{
  componentName: 'Welcome',
    fields: {
      title: {
        value: 'Sitecore Experience Platform + JSS',
      },
      text: {
        value: '<p>...</p>',
      },
      logoImage: {
        value: {
          src: '/assets/img/sc_logo.png',
          alt: 'Logo'
        },
      },
    },
    placeholders: {
      welcome: [
        {
          componentName: 'AnotherComponent',
          fields: {
            /* etc */
          }
        }
      ]
    }
}

```

</details>

<details>
<summary><h2>Input and output binding</h2></summary>

It is possible to bind custom properties on components using the `[inputs]` and `[outputs]` binding properties on `sc-placeholder`. These properties allow input and output binding to properties on all components within the placeholder.

This allows for orchestration/interaction with child components, even though they are created dynamically by `sc-placeholder`, and can be particularly useful in complex user flows, which you still want to manage/optimize through Sitecore XP.

The following is a simplified example of using `[inputs]` and `[outputs]`:

```csharp
import { PlaceholderComponent } from '@sitecore-jss/sitecore-jss/angular';
....
@Component({
  selector: 'my-container-component',
  template: `<sc-placeholder
                name="placeholder"
                [rendering]="rendering"
                [inputs]="inputs"
                [outputs]="outputs"
                ></sc-placeholder>`,
  imports: [PlaceholderComponent] // For Angular 20
})
class MyContainerComponent {
  @Input() public rendering: any;

  inputs = {
    hello: 'world',
    something: () => 'can be really complex'
  };
  outputs = {
    onSomething: (type) => alert(type)
  }
}

@Component({selector: 'my-rendering')
class MyRendering {
  // standard on any placeholder-managed component
  @Input() public rendering: any;

  // additional properties for input/output binding
  @Input() hello: string;
  @Input() something: Function;
  @Output() onSomething = new EventEmitter<string>();
}

```

</details>

<details>
<summary><h2>Lazy loading placeholders</h2></summary>

When lazy loading a Sitecore component, the placeholder appears empty by default. You can specify a temporary body for the component that shows while the component is loading. When the component is finished loading, the temporary body is replaced with the actual content. The following is a simplified example:

```xml
<sc-placeholder [rendering]="rendering">
    <img *scPlaceholderLoading src="loading.gif">
  </sc-placeholder>

```

</details>

<details>
<summary><h2>Enhancing placeholders</h2></summary>

The `Placeholder` component supports several customizations that can enhance the developer experience.

### Error components

If a rendering error occurs in a placeholder, the placeholder displays an error component instead of the placeholder contents and logs the error details to the console. You can customize this component by substituting your own React component using the `errorComponent` prop.

### Missing Component components

If a placeholder contains a rendering name that is unknown to the `componentFactory` (for example, a back-end developer creates a Foo rendering and adds it to a page, but there is no `foo.component.ts` yet), the rendering is replaced with a `MissingComponent` component defined in the `missingComponentComponent` property of your Angular component. The default implementation is a simple message, but you can customize it by using your own Angular component on the `missingComponentComponent` property.

### Handling hidden renderings

If a rendering is hidden by personalization rules, the rendering is replaced by a `HiddenRendering` component to prevent error messages when the rendering is missing a JSS implementation. You can create your own component and provide it to the placeholder using the `hiddenRenderingComponent` property.

</details>