# ngx-skeleton-loader: placeholder screens for Angular apps

> ngx-skeleton-loader is an Angular component that draws animated placeholder shapes where content is still loading, with a global theme, per component overrides and accessibility text built in. It works in standalone and NgModule applications, and its last published npm release was v7.0.0 in 2023, while the repository itself keeps receiving commits.

**willmendesneto/ngx-skeleton-loader** — Make beautiful, animated loading skeletons that automatically adapt to your Angular apps

- Repository: https://github.com/willmendesneto/ngx-skeleton-loader
- Website: https://ngx-skeleton-loader-sample.stackblitz.io
- Stars: 622 · Forks: 52
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/willmendesneto-ngx-skeleton-loader

## A placeholder that looks like the answer

ngx-skeleton-loader is a component for making animated loading skeletons that adapt to an Angular app. The topic tags on the repository name the ancestry: skeleton, loader, content-loader, facebook-loader, ghost.

The idea it implements is that a loading placeholder should look like the content that is about to arrive, so the layout does not jump when the data lands. Instead of a spinner in the middle of an empty page, you render grey shapes in the shape of the list items, cards or avatars, and they animate until the real content replaces them.

The README is explicit about why skeletons rather than a spinner. The idea of the component is to make the process transparent and easier, and the main point is to integrate the component with other tooling processes such as server-side rendering, progressive rendering, and any other you like. It says the integration is transparent for you, and easier to integrate into your application, improving the user experience.

That framing matters for a specific reason. A skeleton screen is only honest if the shape it draws is the shape the real content will have, which means the component has to live in your template next to the real markup, not in a global overlay. That is a constraint on how you use it, and it is the reason the library is a component rather than a service.

The project is MIT licensed, written in TypeScript, published to npm as ngx-skeleton-loader, and there is a live demo on StackBlitz showing animations, appearance and themes, plus a second one simulating a user card component loading.

## Two setup paths, and the one that changed with standalone components

The library supports both Angular application styles, and the README documents them separately.

In a standalone application, you add the provider to the application's app.config.ts:

```typescript
// app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideNgxSkeletonLoader } from 'ngx-skeleton-loader';

export const appConfig: ApplicationConfig = {
  providers: [
    provideNgxSkeletonLoader({
      theme: {
        extendsFromRoot: true,
        height: '30px',
      },
    }),
  ]
};
```

In an NgModule application, you import NgxSkeletonLoaderModule into the module so the components become available in your templates:

```typescript
...
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
...

@NgModule({
  declarations: [
    YourAppComponent
  ],
  imports: [
    ...
    NgxSkeletonLoaderModule,
    ...
  ],
  providers: [],
  bootstrap: [YourAppComponent]
})

export class YourAppComponent {}
```

The `provideNgxSkeletonLoader` function and the `forRoot()` pattern are the two ends of the same idea, and the README's framing of them is telling. Importing the module with `NgxSkeletonLoaderModule.forRoot()` makes it available across the application and lets you globally configure the default values for the components, in case you need different defaults for your app.

The standalone provider is the same idea expressed in the modern style, and it also carries a theme. The `extendsFromRoot: true` flag and the `height` value in that example are the two settings a project reaches for first, because a skeleton that is the wrong height is the most visible way to get this wrong.

## The component in a template, and what count and appearance do

Once the module or provider is in place, the component goes in the template where the real content will eventually live:

```html
<div class="item">
  <ngx-skeleton-loader count="5" appearance="circle" />
</div>
```

`count` sets how many placeholder shapes are rendered, and `appearance` sets the shape. The README's examples use `circle` for a row of avatars and `square` for blocks, and the second example in the NgModule section shows the size input as well:

```html
<div class="item">
  <ngx-skeleton-loader count="5" appearance="circle" />
</div>
<div class="item">
  <!-- It will use `px` as default CSS measure unit -->
  <ngx-skeleton-loader count="5" appearance="square" size="100" />
</div>
```

That comment is worth pausing on, because it is the kind of detail that quietly causes a bug. The size input is a number, and the unit is not configurable there, so the value is interpreted as pixels. If your design system works in rem or em, the placeholder will not match the real content's dimensions, and the layout will still shift when the data arrives.

The other way to use the component is to pass configuration into the component itself. The README describes the `ngx-skeleton-loader` component as handling the skeleton animation and the skeleton styles of your app, and configuration data can optionally be passed into the component directly.

## The theme merge rules are the part that bites

Global theming is where the README spends its most cautionary prose, and the warning is specific enough that you should read it before configuring anything.

By default, when you use `NgxSkeletonLoaderModule.forRoot({ theme: ... })`, the application is using that value as the source of truth, overriding any local theming passed to the `<ngx-skeleton-loader>` component through its `[theme]` input. The README links to steps for changing this behaviour in case you need to.

The combination mode is the way out. Passing `theme: { extendsFromRoot: true, ... }` in the module enables a global theme together with local theme inputs. The rules it then follows are stated as two bullets. By default, every component uses the theme coming from `forRoot()` as the source of truth. And if there is any CSS attribute on the component locally which overrides the CSS spec, it combines both themes, but overriding global CSS attributes in favour of local ones.

In other words, the global theme is the base and the local theme wins on conflict. That is a sensible design, and it is also a design where a local `[theme]` binding can silently change a shape in one place, and a developer reading the global config has to know that a local override exists.

The `extendsFromRoot` flag itself is the switch that says whether the local theme inherits from the global one. So there are two distinct things named similarly: the flag that enables inheritance, and the default that treats the global value as the source of truth. A team that sets a global theme and then wonders why a component ignores its own `[theme]` binding has hit the difference.

## Accessibility is part of the API, not an afterthought

The loadingText and aria-valuetext behaviour is documented with examples, which is unusual for a component of this size and is the strongest argument for adopting the library rather than writing your own CSS.

When you configure the module with `NgxSkeletonLoaderModule.forRoot({ animation: 'pulse', loadingText: 'This item is actually loading...' })`, the components use that animation and that text. The README's example explains that the line will produce the rendering of 5 circles with the pulse animation and the aria-valuetext attribute set with the configured string.

Passing the text directly on the component overrides the global value:

```html
<div class="item">
  <ngx-skeleton-loader count="5" appearance="circle" />
  <!-- above line will produce the rendering of 5 circles with the pulse animation and the aria-valuetext attribute set with "This item is actually loading...", since it was 
  configured via `NgxSkeletonLoaderModule.forRoot()`-->

  <!-- It will render a component with `aria-valuetext` attribute set with `Loading section content`, 
   since it was passed directly via input attribute `loadingText` -->
  <ngx-skeleton-loader count="5" loadingText="Loading section content" />
</div>
```

So the component carries an `aria-valuetext` attribute, which is the accessible name for the value it represents, and the default text says something is loading. A grey animated rectangle with no text is invisible to a screen reader, and a screen reader user gets no signal at all that the page is working.

The author has written a separate post on the subject, titled "NGX-Skeleton-Loader - States, Animations, Performance, and Accessibility for your Angular App", and the README points to it for more detail. The author is the same person behind the component, and that post is where the reasoning about states and performance lives.

## The version gap between the npm tag and the repository

Here is the part of the story that a buyer needs to check for themselves.

The release list for the repository shows v7.0.0 published on 2023-02-04, preceded by v4.0.0 in July 2021 and v3.0.0 a few days earlier. The repository itself is not archived, and the last push was on 2026-10-20, so work has continued in the tree well after the last npm tag.

That gap has two possible explanations and the README visible here does not settle which applies. Either the library reached a stable state and only occasional fixes landed, or the tags stopped being cut while the code kept moving for newer Angular versions. What the repository shows is that the demo app in the same tree is at version 13.0.0 in its package.json, with standalone providers, modern Angular CLI scripts and Playwright end to end tests, which suggests the source is being kept current with a recent Angular even though the published npm package is v7.0.0.

The top level of the tree is worth a look for anyone evaluating it. There is an angular.json workspace with a `projects/` directory holding the library and the demo, an `e2e/` directory, a playwright.config.ts, an eslint.config.mjs, a .bundlesize.config.json for tracking the bundle size, a .circleci directory for CI, and a .coveralls.yml for coverage. The package.json scripts show a test command that runs both the demo and the library suites with coverage, a bundlesize script, a build:ssr and serve:ssr pair for the server side rendering demo, and a release flow that builds the package, updates the library version and pushes tags.

For a team on a current Angular, the practical check is simple: install the published package and see whether it resolves against your Angular version, and read the CHANGELOG.md in the tree for what has changed since v7.0.0.

## The alternative: your own CSS with the same idea

There is a real alternative, and it is the one most teams reach for first, because it costs nothing to try.

A skeleton screen is about a dozen lines of CSS. A container with a background colour, a linear gradient or a pulsing opacity animation, and a `::after` overlay, gives you a grey block that animates. You write that once, put it in a shared component, and you are done. The difference in approach between that and ngx-skeleton-loader is that the library takes over the theming, the animation choices, the accessibility attributes and the shape presets, and gives you configuration through inputs and a global theme rather than through your own stylesheet.

The value of the library is concentrated in the parts you would otherwise get wrong. The `aria-valuetext` handling means the placeholder announces itself to assistive technology, and the global theme with the documented merge rules means a design system can be applied in one place. The `appearance` presets and the `count` input remove the repetitive markup. The animation choices are already made.

The cost is a dependency with a version question attached to it, as the release history shows, and a theming model where the global default wins unless you understand the inheritance flag. If your loading states are simple, a hand-rolled CSS class is a perfectly good answer.

There is a third option worth naming for teams that already use a component library. Many design systems ship their own skeleton or placeholder primitive, wired to the same tokens as the rest of the library, and using that instead keeps one source of truth for spacing and colour. The trade is that you are limited to what that library provides, whereas ngx-skeleton-loader is a standalone component you can add to an application regardless of which component library it uses.

## Conclusion

Adopt ngx-skeleton-loader when your Angular app shows a spinner on a slow API and you want the loading state to look like the page that is coming, and when you can also set the accessibility text so the placeholder announces itself rather than reading as silence. Do not adopt it expecting a current Angular major to be supported without checking, since the last npm release listed is v7.0.0 from 2023-02-04 while the repository is still receiving commits, and confirm the version your Angular version expects rather than the newest tag. Before you ship, read the theme merge rules, because a global theme set through forRoot overrides local ones by default, and check that the loadingText and aria-valuetext defaults say something useful to a screen reader user.

## FAQ

### How do I install ngx-skeleton-loader?

Install it from npm as a project dependency with `npm install ngx-skeleton-loader --save`. The package is published as ngx-skeleton-loader and is MIT licensed.

### How do I use ngx-skeleton-loader in a standalone Angular application?

Add `provideNgxSkeletonLoader` to the providers in your app.config.ts, passing a theme if you want global defaults such as `extendsFromRoot: true` and a `height`. Then put the `<ngx-skeleton-loader>` component in your templates.

### What does the size input of ngx-skeleton-loader use as its unit?

The README's example notes that it will use px as the default CSS measure unit, so a value like size="100" means 100 pixels rather than a value in your design system's unit.

### Does a global theme override the local theme in ngx-skeleton-loader?

By default the theme from NgxSkeletonLoaderModule.forRoot() is the source of truth and overrides local theming passed through the component's theme input. Passing a theme with extendsFromRoot: true enables the combination mode, where local attributes override global ones.

### How does ngx-skeleton-loader handle accessibility?

The component carries an aria-valuetext attribute that is set from the loadingText input or from the value configured globally, so the placeholder announces that content is loading instead of reading as an empty region.

### What is the latest published version of ngx-skeleton-loader?

The release list shows v7.0.0 published on 2023-02-04, with v4.0.0 and v3.0.0 from July 2021. The repository is not archived and the last push was on 2026-10-20, and the demo app in the same tree is at version 13.0.0.

## Sources

- [License: MIT](https://github.com/willmendesneto/ngx-skeleton-loader/blob/main/LICENSE)
- [Project website](https://ngx-skeleton-loader-sample.stackblitz.io)
- [README](https://github.com/willmendesneto/ngx-skeleton-loader/blob/main/README.md)
- [Releases](https://github.com/willmendesneto/ngx-skeleton-loader/releases)
- [willmendesneto/ngx-skeleton-loader on GitHub](https://github.com/willmendesneto/ngx-skeleton-loader)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/willmendesneto-ngx-skeleton-loader
