---
title: Components vs. Plugins in ROS 2
description: In this post we try to understand the difference between ROS 2 plugins
  and components. We discuss their implementations, use and showcase an example.
url: https://canonical.com/blog/components-vs-plugins-in-ros-2?format=md
---

1. [Blog](https://canonical.com/blog)
2. Article

---

[Jeremie Deray](https://canonical.com/blog/author/artivis "More about Jeremie Deray")

27 August 2019

# Components vs. Plugins in ROS 2

[components](https://canonical.com/blog/tag/components)
[plugins](https://canonical.com/blog/tag/plugins)
[robotics](https://canonical.com/blog/tag/robotics)
[ROS2](https://canonical.com/blog/tag/ros2)

---

Share the article
> Please note that this blog post has old information that may no longer be correct. We invite you to read the content as a starting point but please search for more updated information in the [ROS documentation](https://docs.ros.org/).

After our series of post about ROS 2 CLI tools ([1](https://ubuntu.com/blog/ros-2-command-line-interface), [2](https://ubuntu.com/blog/creating-a-ros-2-cli-command-and-verb/)), we continue exploring the ROS 2 realm taking a look at ROS 2 components and more specifically, how they compare to plugins.

*spoiler alert:*

> ***Long story short, components are plugins.***

Short story long? Is that a thing?

Well plugins and components are indeed essentially the same thing. Down the road, both are built into respective shared libraries, neither have a `main` function, they both are loaded at runtime and used by a third party.

We’ll note here that while [plugins](http://wiki.ros.org/pluginlib) come straight outta ROS 1, components on the other hand are the ROS 2 evolution of ROS 1 nodelets ([what’s that?](http://wiki.ros.org/nodelet)) after being exposed to a [Fire Stone](https://pokemondb.net/item/fire-stone) on a full moon. Same idea, different beasts.

## Plugins vs. Components

So what are the actual differences? To put it simply, a component is a plugin which derives from a ROS 2 node.

This assertion is backed by the fact the both rely on the `class_loader` [package](https://github.com/ros/class_loader), a ROS-independent library for dynamic class introspection and loading from runtime libraries. Their respective internal plugin-related plumbing (factory, registration, library path finding, loading etc.) is managed underneath by `class_loader`. Both offer a macro-based helper for registration, and both macros resolve to `class_loader`‘s `CLASS_LOADER_REGISTER_CLASS` macro. Yeah they immediately resolve to it, I mean, they don’t even try to hide it. On the other hand, why would they?

For a traditional plugin, the base class can be anything, user defined or not. The only constraint is that the derived class has to be default constructible (therefore, by the law of C++, the base class too). In the case of components, the plugin class commonly derives from the `rclcpp::Node` class, but it is not required. Indeed, the requirements for a class to be exported as a component are,

* Have a constructor that takes a single argument that is a `rclcpp::NodeOptions` instance.
* Have a method of the signature:
  + `rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface(void)`

which includes `rclcpp::Node` of course, but also e.g. `rclcpp_lifecycle::LifecycleNode`. It means that a component can inherit from any class meeting those requirements, but it can also implement them itself and skip inheritance altogether.

That last point is an important difference between plugins and components.

## Components are special plugins

Indeed, traditionally plugins must declare their base classes when registering to `class_loader` factory scheme. This allows the factory to instantiate the derived object to a smart-pointer of base type which in turn can be delivered to the user, the developer.

```
// Registering a plugin
PLUGINLIB_EXPORT_CLASS(BaseClass, DerivedClass)
//
...
// Instantiating a plugin
// We have access to all of BaseClass API
std::shared_ptr<BaseClass> base_ptr = plugin_loader.createSharedInstance("DerivedClass");
```

Components’ base class registration on the other hand was developed relying on `void` (smart) pointer-based type-erasure. Thus allowing for a simple wrapper class as a common base class to all components. This design implies that the developer is not expected to query the factory for a new instance by himself. What would you do with a `void` pointer anyway? Instead the factory serves intended agents such as the `rclcpp_components::ComponentManager` and `rclcpp_components::ComponentContainer`.

```
// Registering a component
RCLCPP_COMPONENTS_REGISTER_NODE(DerivedNode)
//
...
// NodeInstanceWrapper is a wrapper around both a
// std::shared_ptr<void> and a
// rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
// Not much to do with those!
rclcpp_components::NodeInstanceWrapper component_wrapper = component_loader->create_node_instance(options);
```

In most cases, components inherit from `rclcpp::Node` as it is the easiest way to fulfill the above requirements. Therefore, we’ll assume that a component **is** a ROS 2 node, with everything it involves, possibly parameters, listeners/publishers, services, action, et tutti quanti.

We can therefore make an important distinction here: components are plugins **with a ROS 2 interface**. This draws an important line as when and where to use plugins or components.

## Plugins or components, that is the question

The answer to this question obviously depends on your project. But first, let me assume that you do have a need for plugins and spare you asking

‘Do you really need to use plugins?’

(see what I did there).

As a general rule, I’d recommend for you to use components over plugins
whenever possible. The reason is simple, you, as a components developer, do not have to deal with the plugin aspect of things beside the registration macro. No plugin name as a ROS parameter, no plugin loader, no exception handling etc. Then from a user perspective it is clearer I believe to launch a specific node, say `my_plugin_b_node`, rather than launching an interface node given a plugin’s name as a ROS parameter `my_node plugin_name:='PluginB'`. It also spares the frustration of seeing the node crash or being stall because the plugin is not found or misspelled.

Note that this recommendation does not prevent you from enforcing some common API the same manner it would arise from using plugins. Your components can inherit from a base class of your making which enforce this API. The base class may also implement the ROS interface, abstracting it away, leaving to the derived class a simple `virtual doMath(Arg)` -like function to define.

> But my stuff is so modular that I’m using several plugins

Can you possibly rethink the design? Can you break it down in several small nodes that communicate through ROS interfaces? I’d bet that some of those intermediate data could be useful somewhere else in your system too.

If not, or if you find yourself with a different use case which does require plugin, you may still declare your node class as a component! Components and plugins are not mutually exclusive. Moreover,

> [In ROS 2 [components is] the recommended way of writing your code.](https://index.ros.org/doc/ros2/Tutorials/Composition/)

## Show me some code

While doing the legwork for this post, I wrote a small ROS 2 package which has no other purpose than being a goto example on how to write a component or a plugin in ROS 2. You may find it on github: [demo\_plugin\_component](https://github.com/artivis/ros2_playground/tree/master/demo_plugin_component). This example showcase the use of plugins in conjunction with components, look for one and get both. After compiling, one may run it in either way,

Like a plain node,

```
$ ros2 run demo_plugin_component talker_node __params:=demo_plugin_component/cfg/params.yaml
```

As a component in a component container,

```
ros2 run rclcpp_components component_container
```

```
ros2 component load /ComponentManager demo_plugin_component ros2_playground::TalkerNode -p writter_name:='ros2_playground::MessageWritterDerived'
```

As a component in a component container, from a launch file,

```
ros2 launch demo_plugin_component talker.launch.py
```

Do not forget to run a `topic echo` to monitor the node output, it is a talker after all,

```
ros2 topic echo /chatter
```

[Get in touch

Interested in running Ubuntu in your organization?](https://ubuntu.com/about/contact-us/form)

## Sign up for our newsletter

Get the latest Canonical news and updates in your inbox.

Work email:

\*I agree to receive information about Canonical's
products and services.

By submitting this form, I confirm that I have read and agree to [Canonical's Privacy Policy](https://canonical.com/legal/dataprivacy).

Sign up

## Share on

---

## Related posts

[### Extending ROS Noetic Support with ESM-Enabled Content Snaps](https://canonical.com/blog/extending-ros-noetic-support-with-esm-enabled-content-snaps)

Canonical has now extended its ESM (Expanded Security Maintenance) for ROS coverage to ROS Noetic content-sharing snaps. With ESM for ROS now available in both deb and snap...

[Florencia Cabral Berenfus](https://canonical.com/blog/author/flor-cabral)

17 December 2025

[### Discover your fully open source robotics observability at ROSCon 2025](https://canonical.com/blog/roscon-2025)

Another year, another ROSCon! This year we’re heading to Singapore, and Canonical is once again thrilled to sponsor this important community event. Just like last year in...

[Gabriel Aguiar Noury](https://canonical.com/blog/author/g-aguiar-noury)

22 October 2025

[### Canonical is now a platinum member in the Open Source Robotics Alliance](https://canonical.com/blog/osra-membership)

Ubuntu is the home of ROS. The very first ROS distribution, Box Turtle, launched on Ubuntu 8.04 LTS, Hardy Heron, and since then, Ubuntu and ROS have grown hand in hand. With...

[Gabriel Aguiar Noury](https://canonical.com/blog/author/g-aguiar-noury)

20 August 2025

[### ROS Noetic is EOL – take action to maintain fleet security](https://canonical.com/blog/ros-noetic-is-eol-take-action-to-maintain-fleet-security)

As of May 2025, the Robot Operating System (ROS) Noetic Ninjemys officially reached its end of life (EOL). First released in 2020 as the final ROS (1) distribution, ROS Noetic...

[Florencia Cabral Berenfus](https://canonical.com/blog/author/flor-cabral)

8 August 2025
