Key Takeaways
- A workspace contains configured Angular CLI projects; those projects can be applications or libraries.
- An application is a runnable product. A library is separately buildable reusable code with a public API, not an application you deploy on its own.
- NgModules organize functionality inside applications and libraries. They aren't another kind of workspace project.
- New CLI applications and libraries use standalone APIs by default, so neither requires an NgModule. Publishing a library to npm is optional.
An Angular workspace is the container, applications and libraries are projects inside it, and NgModules are an internal organization mechanism. These terms describe different levels of structure—not competing ways to package the same thing. Standalone Angular changes how you organize project internals, not that hierarchy.
Angular Workspace vs Library vs Application at a Glance
| Concept | Purpose | Typical location | Build output | Required? |
|---|---|---|---|---|
| Workspace | Configure and manage CLI projects | Root containing angular.json | No application or package output of its own | For the standard Angular CLI project workflow |
| Application | Run a product or independently deployable UI | Root src/ or projects/myapp/ | Browser assets; server or prerendered output when configured | Not in a library-only workspace |
| Library | Provide reusable code through a public API | projects/mylibrary/ | Consumable package | No |
| NgModule | Group declarations, imports, exports, and providers | Inside an application or library | Compiled as part of its project | No |
| Standalone component | Define a UI unit with its own template dependencies | Inside an application or library | Compiled as part of its project | Recommended for new code, not mandatory |
The CLI project types are application and library. Neither an NgModule nor a standalone component is a sibling project type.
What Is an Angular Workspace?
A workspace brings project configuration, dependencies, and development tooling together. Its root angular.json identifies the applications and libraries managed by the CLI. Multi-project workspace support arrived with Angular CLI 6; that's historical context, not a requirement to structure modern code around NgModules.
A workspace and a monorepo aren't synonyms. A workspace is a CLI configuration boundary; a monorepo is a repository containing multiple projects. A multi-project workspace can implement a monorepo, but repository organization and additional monorepo tooling are separate decisions.
Create a Workspace, Library, and Application
This walkthrough uses Angular CLI 22.2.3. Start with Node.js and npm installed, and check Angular's runtime compatibility table before installing the CLI.
npm install -g @angular/cli@22.2.3
ng new myworkspace --no-create-application
cd myworkspace
ng generate library mylibrary
ng generate application myapp
ng build mylibrary
ng build myappWith the standard generated configuration, both projects go under projects/. The application and library generators default to standalone APIs. Building the library produces its package; building the application produces application deployment output.
Alternatively, ng new myworkspace creates an initial application named myworkspace, with its source in the root src/ directory. Additional applications and libraries go under projects/ by default. Starting without an application keeps every project's layout consistent; options and workspace settings can change those locations.
myworkspace/
|-- angular.json
|-- package.json
|-- tsconfig.json
`-- projects/
|-- myapp/ application project
| `-- src/ components, services, bootstrap code
`-- mylibrary/ library project
|-- ng-package.json
|-- package.json
`-- src/
|-- public-api.ts
`-- lib/ reusable implementation
myapp --imports--> mylibrary
NgModules and standalone components belong INSIDE these projects.Angular Library vs Application: Execution and Packaging
An application supplies the entry points and configuration needed to run your product. A generated browser application includes src/main.ts for startup and src/index.html as its HTML document. But “application” doesn't mean browser-only: Angular supports server-side rendering, which renders HTML on a server, and prerendering, which generates HTML during the build.
A library doesn't launch a product on its own. It exposes reusable code to a consuming application: components, directives, pipes, services, utilities, or types. You can consume it within the same workspace or distribute a built package elsewhere.
For a CLI-generated library, ng-package.json configures packaging, including its public entry file. The conventional filename is src/public-api.ts, with a hyphen—not public_api.ts. Built package metadata directs consumers to the packaged entry points and type definitions.
Application deployment and library distribution are separate workflows. For the application side, see deploying apps with the Angular CLI.
Understand angular.json and Build Targets
A target is a named task, such as building or testing a project. A builder is the implementation that performs that task. In angular.json, targets live under each project's architect section and can have default options and named configurations.
angular.jsondefines project names, roots, project types, targets, and generation defaults.- The root
package.jsonmanages dependencies available across the workspace. - The root
tsconfig.jsonsupplies shared TypeScript configuration; project-specific configurations refine it. - A library's own
package.jsondescribes its package dependencies and metadata.
For compiler configuration background, see our guide to tsconfig.
Rather than copying an incomplete build configuration, inspect what your CLI generated. These read-only queries show the project types and build implementations:
ng config projects.myapp.projectType
ng config projects.myapp.architect.build.builder
ng config projects.mylibrary.projectType
ng config projects.mylibrary.architect.build.builderFor the CLI 22.2.3 walkthrough, the corresponding values are:
"application"
"@angular/build:application"
"library"
"@angular/build:ng-packagr"Angular Module vs Library: Where Standalone APIs Fit
Here, “Angular module” means NgModule: a class decorated with @NgModule. That's different from a JavaScript module, which uses language-level import and export statements. Standalone Angular code still uses JavaScript modules.
An NgModule's metadata can declare non-standalone components, directives, and pipes; import dependencies; export functionality for templates; and configure dependency-injection providers. An NgModule isn't an independently packaged CLI project, and a filename ending in .module.ts isn't what gives it its behavior—the decorator does.
Angular recommends standalone components for new code. A standalone component lists its template dependencies in its own imports array, without being declared in an NgModule. A standalone application can start through bootstrapApplication, which renders a standalone root component.
Existing NgModule-based code remains supported, and the two styles interoperate: an NgModule can import standalone components, while a standalone component can import an NgModule. A library can therefore expose standalone APIs, NgModules, or both. Its package boundary is independent of that choice.
Consume a Library in the Same Workspace
The standard CLI workflow uses the library's built output. The library generator adds a TypeScript path mapping for its package name; in this example, mylibrary resolves to ./dist/mylibrary. You don't need to publish it or install it back from npm.
Add this utility to projects/mylibrary/src/lib/greeting.ts:
export function greeting(name: string): string {
return `Hello, ${name}!`;
}Append this export to projects/mylibrary/src/public-api.ts, keeping the generated exports:
// Public API fragment: append to the existing file.
export { greeting } from './lib/greeting';Replace projects/myapp/src/app/app.ts with this minimal root component:
import { Component } from '@angular/core';
import { greeting } from 'mylibrary';
@Component({
selector: 'app-root',
standalone: true,
template: '<h1>{{ message }}</h1>',
})
export class App {
protected readonly message = greeting('StackChief');
}The generated startup code already bootstraps App. Update the generated welcome-template tests to reflect the replacement component. A utility function needs only a TypeScript import; a standalone component used in a template also belongs in the consuming component's imports array.
Build the updated library before building or serving its consumer:
ng build mylibrary
ng build myapp
ng serve myappDuring development, run ng build mylibrary --watch in another terminal to rebuild library changes. Don't redirect the package mapping to projects/mylibrary/src: application builds and library packaging use different pipelines, and Angular's documented workflow points consumers at the built library.
Choose the Project Explicitly When Generating Code
For component generation, --project selects a project name from angular.json; it isn't a filesystem path. Without it, the component generator determines the project from the current directory. Be explicit in multi-project workspaces:
ng generate component mycomponent --project=myapp
ng generate component mycomponent --project=mylibraryThese commands add components to different projects, not new projects. Generation alone doesn't make a library component part of its supported package API; export it through the public entry point when consumers should use it.
Options depend on the schematic and project type. For example, ng build myapp and ng build mylibrary invoke different builders and don't accept identical build options. A library isn't an application that happens to lack an HTML page.
When Should You Create a Library?
Start with a feature folder when the code belongs to one application and doesn't need a separate contract. Create a library when multiple consumers need the same capability, or when an explicit public API and separately verifiable build are worth maintaining.
- Use an application for a product with its own startup and deployment lifecycle.
- Use a library for a reusable capability that shouldn't depend on one consuming application's internals.
- Use standalone components and feature folders to organize new code inside either project type.
- Keep multiple projects in one workspace when shared dependencies and coordinated changes fit your team's workflow.
Don't extract a library just because a folder is large. Prefer a small, coherent public surface and a one-way dependency: applications depend on libraries, not the reverse. Library boundaries add maintenance work; reuse should justify it.
Common Mistakes
- Confusing NgModule exports with package exports. Making a component available through an NgModule doesn't replace exposing the intended API through the library entry point.
- Skipping the library build. After a clean checkout, build local libraries before their consuming applications.
- Importing private source paths. Import from the supported package entry point rather than reaching into another project's implementation files.
- Treating publishing as deployment. Before distribution, review package metadata, versions, compatibility, documentation, and release checks.
For distribution, build the library with ng build mylibrary --configuration production and distribute the resulting package, not its source directory. Angular Package Format libraries use partial compilation for publication; Angular dependencies required by the library should be peer dependencies. Keep the generated production configuration unless you understand the packaging consequences of changing it.
FAQ
What is the difference between an Angular workspace and an Angular project?
A workspace is the configuration container managed by the Angular CLI. A project is an application or library registered inside that workspace; its configured name is how you select it for commands.
Can an Angular workspace contain multiple applications and libraries?
Yes. One workspace can contain multiple applications and libraries, and an application can consume multiple libraries. Each project has its own configuration under the projects section of angular.json.
Do I need an NgModule in a new Angular application?
No. New CLI applications use standalone APIs by default, and Angular recommends standalone components for new code. Existing NgModule-based applications remain supported.
Do Angular libraries have to be published to npm?
No. In the standard CLI workspace workflow, build the library and import it by its package name using the generated TypeScript path mapping. Publishing is only needed when your distribution workflow calls for it.
How do I generate an application or library in an existing Angular workspace?
From inside the workspace, run ng generate application myapp or ng generate library mylibrary. These commands add new project entries to angular.json and place their files under projects/ by default.
Further Reading
- Workspace and project file structure — root files, application files, and library layout.
- Angular workspace configuration — project entries, targets, builders, and configurations.
- ng new command reference — empty workspaces and initial application options.
- Library generator reference — public entry files, path mappings, and standalone options.
- Creating Angular libraries — public APIs, local consumption, builds, and publishing.
- NgModules guide — declarations, imports, exports, and interoperability.
- Server-side and hybrid rendering — client rendering, SSR, and prerendering.
- Angular Package Format — built entry points, metadata, and partial compilation.
