Imagine yourself at a movie theater. No matter what theater you go to, or what movie you see, you'll notice some very similar things - most notably, the large screen the movie plays on. This screen is an excellent example of a reusable object - it can show any movie projected onto it! The screen doesn't need to know the details of the movie it's playing - all it does is show whatever content is projected onto it.
This is a similar idea to how content projection works in web development! Today, we'll go over what content projection looks like and how you can use it to make components that are reusable and less tied to the data they display.
We'll be using Angular for these examples, but any framework you'd like to use likely has a way to do this as well!
If you'd like to view the source code for these examples, you can check them out in this Stackblitz editor.
🔗Single slot projection
The simplest way to do content projection is to use a single "slot" or area where we pass data in. Note that you're not limited on what data you pass in - you could provide as much data as you wanted in here. But your component that's handling the content projection uses a single slot for all that data.
Using the movie example above, let's create a simple "screen" that will display a random cat GIF, and some "now playing" text with it.
For the screen component, the HTML will be very simple - just a section container and a special Angular tag called
ng-content. This element acts as a placeholder for the content that will be passed into it, and does not create a DOM element itself. It tells the component that we expect some data in an unknown shape to be shown in it's place.
// single-screen.component.html <section> <ng-content></ng-content> </section>
Then, in our main app component, we can send this screen the data we want it to show!
// app.component.html <app-single-screen> <div class="movie"> <img src="https://cataas.com/cat/gif" /> </div> <div class="now-playing"> <h2>Now Playing: Random Cat GIF!</h2> </div> </app-single-screen>
And that's it - our
single-screen component will display the GIF and title!
Sometimes, it can be useful to designate some of the data that gets projected into certain sections of our component. In the previous example, we could have the "Now Playing" text set up almost anywhere - it could be on the top of the screen, the bottom, over the image itself. Even though we're projecting data into our component, we can designate some sections with a particular name, so when we write the content that gets sent to the component, we can tell it where to go.
If we wanted to set up our screen so that it always shows the name first and then the image in our component, we would specify the
select value on the
ng-content tag. If we want one of these tags to be the default value, we can leave its
select value empty.
// multi-screen.component.html <section> <ng-content></ng-content> <ng-content select="[movie]"></ng-content> </section>
Then, when we add this tag to our main app, we'll see the data in the order our component says it should show, no matter what order we type it in.
// app.component.html <app-multi-screen> <div class="movie" movie> <img src="https://cataas.com/cat/gif?tags=silly" /> </div> <div class="now-playing"> <h2>Now Playing: Random Cat GIF!</h2> </div> </app-multi-screen>
This topic can be as simple or as complex as you need it to. There's tons of great use cases for content projection. You could use it to create reusable layouts, as an expansion toggle wrapper around different lists of data, or to create reusable designed pieces that can take in whatever type of data you'd like. If you're interested in some more complex examples, I'd highly recommend checking out the Angular documentation for this subject. Can you think of other great use cases? Let us know!
This Dot Labs is a development consultancy focused on providing staff augmentation, architectural guidance, and consulting to companies.
We help implement and teach modern web best practices with technologies such as React, Angular, Vue, Web Components, GraphQL, Node, and more.