diff --git a/esphome/components/button/button.cpp b/esphome/components/button/button.cpp
index 3d58780d6d..dfa417de7b 100644
--- a/esphome/components/button/button.cpp
+++ b/esphome/components/button/button.cpp
@@ -6,9 +6,6 @@ namespace button {
 
 static const char *const TAG = "button";
 
-Button::Button(const std::string &name) : EntityBase(name) {}
-Button::Button() : Button("") {}
-
 void Button::press() {
   ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
   this->press_action();
diff --git a/esphome/components/button/button.h b/esphome/components/button/button.h
index 3b5f338887..a4902810b2 100644
--- a/esphome/components/button/button.h
+++ b/esphome/components/button/button.h
@@ -28,9 +28,6 @@ namespace button {
  */
 class Button : public EntityBase {
  public:
-  explicit Button();
-  explicit Button(const std::string &name);
-
   /** Press this button. This is called by the front-end.
    *
    * For implementing buttons, please override press_action.
diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp
index 37572ae913..b4d5ee9685 100644
--- a/esphome/components/climate/climate.cpp
+++ b/esphome/components/climate/climate.cpp
@@ -453,12 +453,7 @@ void Climate::set_visual_temperature_step_override(float target, float current)
   this->visual_target_temperature_step_override_ = target;
   this->visual_current_temperature_step_override_ = current;
 }
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-Climate::Climate(const std::string &name) : EntityBase(name) {}
-#pragma GCC diagnostic pop
 
-Climate::Climate() : Climate("") {}
 ClimateCall Climate::make_call() { return ClimateCall(this); }
 
 ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h
index 520036f718..43bd71657d 100644
--- a/esphome/components/climate/climate.h
+++ b/esphome/components/climate/climate.h
@@ -166,11 +166,6 @@ struct ClimateDeviceRestoreState {
  */
 class Climate : public EntityBase {
  public:
-  /// Construct a climate device with empty name (will be set later).
-  Climate();
-  /// Construct a climate device with a name.
-  Climate(const std::string &name);
-
   /// The active mode of the climate device.
   ClimateMode mode{CLIMATE_MODE_OFF};
   /// The active state of the climate device.
diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp
index 902e9bca94..24dd88b698 100644
--- a/esphome/components/cover/cover.cpp
+++ b/esphome/components/cover/cover.cpp
@@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
   }
 }
 
-Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
+Cover::Cover() : position{COVER_OPEN} {}
 
 CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
 CoverCall &CoverCall::set_command(const char *command) {
@@ -204,7 +204,6 @@ optional<CoverRestoreState> Cover::restore_state_() {
     return {};
   return recovered;
 }
-Cover::Cover() : Cover("") {}
 std::string Cover::get_device_class() {
   if (this->device_class_override_.has_value())
     return *this->device_class_override_;
diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h
index 8ae9cfc8dd..c6a420fa97 100644
--- a/esphome/components/cover/cover.h
+++ b/esphome/components/cover/cover.h
@@ -111,7 +111,6 @@ const char *cover_operation_to_str(CoverOperation op);
 class Cover : public EntityBase {
  public:
   explicit Cover();
-  explicit Cover(const std::string &name);
 
   /// The current operation of the cover (idle, opening, closing).
   CoverOperation current_operation{COVER_OPERATION_IDLE};
diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp
index 4a53748213..e4020a902e 100644
--- a/esphome/components/esp32_camera/esp32_camera.cpp
+++ b/esphome/components/esp32_camera/esp32_camera.cpp
@@ -202,7 +202,7 @@ void ESP32Camera::loop() {
 float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; }
 
 /* ---------------- constructors ---------------- */
-ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
+ESP32Camera::ESP32Camera() {
   this->config_.pin_pwdn = -1;
   this->config_.pin_reset = -1;
   this->config_.pin_xclk = -1;
@@ -215,7 +215,6 @@ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
 
   global_esp32_camera = this;
 }
-ESP32Camera::ESP32Camera() : ESP32Camera("") {}
 
 /* ---------------- setters ---------------- */
 /* set pin assignment */
diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h
index 62fdbabd06..5f88c6fda8 100644
--- a/esphome/components/esp32_camera/esp32_camera.h
+++ b/esphome/components/esp32_camera/esp32_camera.h
@@ -103,7 +103,6 @@ class CameraImageReader {
 /* ---------------- ESP32Camera class ---------------- */
 class ESP32Camera : public Component, public EntityBase {
  public:
-  ESP32Camera(const std::string &name);
   ESP32Camera();
 
   /* setters */
diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp
index c8a3064f6c..87566bad4a 100644
--- a/esphome/components/fan/fan.cpp
+++ b/esphome/components/fan/fan.cpp
@@ -80,9 +80,6 @@ void FanRestoreState::apply(Fan &fan) {
   fan.publish_state();
 }
 
-Fan::Fan() : EntityBase("") {}
-Fan::Fan(const std::string &name) : EntityBase(name) {}
-
 FanCall Fan::turn_on() { return this->make_call().set_state(true); }
 FanCall Fan::turn_off() { return this->make_call().set_state(false); }
 FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
diff --git a/esphome/components/fan/fan.h b/esphome/components/fan/fan.h
index 337bb3935a..f9d317e675 100644
--- a/esphome/components/fan/fan.h
+++ b/esphome/components/fan/fan.h
@@ -99,10 +99,6 @@ struct FanRestoreState {
 
 class Fan : public EntityBase {
  public:
-  Fan();
-  /// Construct the fan with name.
-  explicit Fan(const std::string &name);
-
   /// The current on/off state of the fan.
   bool state{false};
   /// The current oscillation state of the fan.
diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h
index 044ee59736..5926e700b0 100644
--- a/esphome/components/fan/fan_state.h
+++ b/esphome/components/fan/fan_state.h
@@ -15,7 +15,6 @@ enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection
 class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component {
  public:
   FanState() = default;
-  explicit FanState(const std::string &name) : Fan(name) {}
 
   /// Get the traits of this fan.
   FanTraits get_traits() override { return this->traits_; }
diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp
index 64c29a346b..50ebd8882b 100644
--- a/esphome/components/light/light_state.cpp
+++ b/esphome/components/light/light_state.cpp
@@ -8,7 +8,6 @@ namespace light {
 
 static const char *const TAG = "light";
 
-LightState::LightState(const std::string &name, LightOutput *output) : EntityBase(name), output_(output) {}
 LightState::LightState(LightOutput *output) : output_(output) {}
 
 LightTraits LightState::get_traits() { return this->output_->get_traits(); }
diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h
index 81f8be7207..ac4718ade5 100644
--- a/esphome/components/light/light_state.h
+++ b/esphome/components/light/light_state.h
@@ -33,9 +33,6 @@ enum LightRestoreMode {
  */
 class LightState : public EntityBase, public Component {
  public:
-  /// Construct this LightState using the provided traits and name.
-  LightState(const std::string &name, LightOutput *output);
-
   LightState(LightOutput *output);
 
   LightTraits get_traits();
diff --git a/esphome/components/lock/lock.cpp b/esphome/components/lock/lock.cpp
index dbb8a941ad..ddc5445349 100644
--- a/esphome/components/lock/lock.cpp
+++ b/esphome/components/lock/lock.cpp
@@ -24,8 +24,7 @@ const char *lock_state_to_string(LockState state) {
   }
 }
 
-Lock::Lock(const std::string &name) : EntityBase(name), state(LOCK_STATE_NONE) {}
-Lock::Lock() : Lock("") {}
+Lock::Lock() : state(LOCK_STATE_NONE) {}
 LockCall Lock::make_call() { return LockCall(this); }
 
 void Lock::lock() {
diff --git a/esphome/components/lock/lock.h b/esphome/components/lock/lock.h
index 52256691f6..7a98187a4f 100644
--- a/esphome/components/lock/lock.h
+++ b/esphome/components/lock/lock.h
@@ -103,7 +103,6 @@ class LockCall {
 class Lock : public EntityBase {
  public:
   explicit Lock();
-  explicit Lock(const std::string &name);
 
   /** Make a lock device control call, this is used to control the lock device, see the LockCall description
    * for more info.
diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py
index 73cda2c926..8e45915cf3 100644
--- a/esphome/components/partition/light.py
+++ b/esphome/components/partition/light.py
@@ -102,7 +102,7 @@ async def to_code(config):
                 conf[CONF_ADDRESSABLE_LIGHT_ID],
                 await cg.get_variable(conf[CONF_SINGLE_LIGHT_ID]),
             )
-            light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], "", wrapper)
+            light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], wrapper)
             await cg.register_component(light_state, conf)
             segments.append(AddressableSegment(light_state, 0, 1, False))
 
diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp
index 0de452b784..fc66e03d6b 100644
--- a/esphome/components/sensor/sensor.cpp
+++ b/esphome/components/sensor/sensor.cpp
@@ -20,8 +20,7 @@ std::string state_class_to_string(StateClass state_class) {
   }
 }
 
-Sensor::Sensor(const std::string &name) : EntityBase(name), state(NAN), raw_state(NAN) {}
-Sensor::Sensor() : Sensor("") {}
+Sensor::Sensor() : state(NAN), raw_state(NAN) {}
 
 std::string Sensor::get_unit_of_measurement() {
   if (this->unit_of_measurement_.has_value())
diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h
index e3651752cf..4308eafb12 100644
--- a/esphome/components/sensor/sensor.h
+++ b/esphome/components/sensor/sensor.h
@@ -57,7 +57,6 @@ std::string state_class_to_string(StateClass state_class);
 class Sensor : public EntityBase {
  public:
   explicit Sensor();
-  explicit Sensor(const std::string &name);
 
   /// Get the unit of measurement, using the manual override if set.
   std::string get_unit_of_measurement();
diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp
index caa072b1ea..72e7add158 100644
--- a/esphome/components/switch/switch.cpp
+++ b/esphome/components/switch/switch.cpp
@@ -6,8 +6,7 @@ namespace switch_ {
 
 static const char *const TAG = "switch";
 
-Switch::Switch(const std::string &name) : EntityBase(name), state(false) {}
-Switch::Switch() : Switch("") {}
+Switch::Switch() : state(false) {}
 
 void Switch::turn_on() {
   ESP_LOGD(TAG, "'%s' Turning ON.", this->get_name().c_str());
diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h
index b89d8db6a5..8bea3b36db 100644
--- a/esphome/components/switch/switch.h
+++ b/esphome/components/switch/switch.h
@@ -32,7 +32,6 @@ enum SwitchRestoreMode {
 class Switch : public EntityBase {
  public:
   explicit Switch();
-  explicit Switch(const std::string &name);
 
   /** Publish a state to the front-end from the back-end.
    *
diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp
index d76ab7e27d..f10cd50267 100644
--- a/esphome/components/text_sensor/text_sensor.cpp
+++ b/esphome/components/text_sensor/text_sensor.cpp
@@ -6,9 +6,6 @@ namespace text_sensor {
 
 static const char *const TAG = "text_sensor";
 
-TextSensor::TextSensor() : TextSensor("") {}
-TextSensor::TextSensor(const std::string &name) : EntityBase(name) {}
-
 void TextSensor::publish_state(const std::string &state) {
   this->raw_state = state;
   this->raw_callback_.call(state);
diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h
index cf7b6b86ef..996af02f7e 100644
--- a/esphome/components/text_sensor/text_sensor.h
+++ b/esphome/components/text_sensor/text_sensor.h
@@ -30,9 +30,6 @@ namespace text_sensor {
 
 class TextSensor : public EntityBase {
  public:
-  explicit TextSensor();
-  explicit TextSensor(const std::string &name);
-
   /// Getter-syntax for .state.
   std::string get_state() const;
   /// Getter-syntax for .raw_state
diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp
index 49a73854a1..19ab57349d 100644
--- a/esphome/core/entity_base.cpp
+++ b/esphome/core/entity_base.cpp
@@ -6,16 +6,14 @@ namespace esphome {
 
 static const char *const TAG = "entity_base";
 
-EntityBase::EntityBase(std::string name) : name_(std::move(name)) { this->calc_object_id_(); }
-
 // Entity Name
-const std::string &EntityBase::get_name() const { return this->name_; }
-void EntityBase::set_name(const std::string &name) {
-  if (name.empty()) {
-    this->name_ = App.get_friendly_name();
+const StringRef &EntityBase::get_name() const { return this->name_; }
+void EntityBase::set_name(const char *name) {
+  this->name_ = StringRef(name);
+  if (this->name_.empty()) {
+    this->name_ = StringRef(App.get_friendly_name());
     this->has_own_name_ = false;
   } else {
-    this->name_ = name;
     this->has_own_name_ = true;
   }
   this->calc_object_id_();
diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h
index 5ab1f7b424..e25aab21a9 100644
--- a/esphome/core/entity_base.h
+++ b/esphome/core/entity_base.h
@@ -2,6 +2,7 @@
 
 #include <string>
 #include <cstdint>
+#include "string_ref.h"
 
 namespace esphome {
 
@@ -14,12 +15,9 @@ enum EntityCategory : uint8_t {
 // The generic Entity base class that provides an interface common to all Entities.
 class EntityBase {
  public:
-  EntityBase() : EntityBase("") {}
-  explicit EntityBase(std::string name);
-
   // Get/set the name of this Entity
-  const std::string &get_name() const;
-  void set_name(const std::string &name);
+  const StringRef &get_name() const;
+  void set_name(const char *name);
 
   // Get whether this Entity has its own name or it should use the device friendly_name.
   bool has_own_name() const { return this->has_own_name_; }
@@ -54,7 +52,7 @@ class EntityBase {
   virtual uint32_t hash_base() { return 0L; }
   void calc_object_id_();
 
-  std::string name_;
+  StringRef name_;
   bool has_own_name_{false};
   std::string object_id_;
   const char *icon_c_str_{nullptr};
diff --git a/esphome/core/string_ref.cpp b/esphome/core/string_ref.cpp
new file mode 100644
index 0000000000..ce1e33cbb7
--- /dev/null
+++ b/esphome/core/string_ref.cpp
@@ -0,0 +1,12 @@
+#include "string_ref.h"
+
+namespace esphome {
+
+#ifdef USE_JSON
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+void convertToJson(const StringRef &src, JsonVariant dst) { dst.set(src.c_str()); }
+
+#endif  // USE_JSON
+
+}  // namespace esphome
diff --git a/esphome/core/string_ref.h b/esphome/core/string_ref.h
new file mode 100644
index 0000000000..5940a7ee65
--- /dev/null
+++ b/esphome/core/string_ref.h
@@ -0,0 +1,134 @@
+#pragma once
+
+#include <string>
+#include <iterator>
+#include <cstring>
+#include "esphome/core/defines.h"
+
+#ifdef USE_JSON
+#include "esphome/components/json/json_util.h"
+#endif  // USE_JSON
+
+namespace esphome {
+
+/**
+ * StringRef is a reference to a string owned by something else.  So it behaves like simple string, but it does not own
+ * pointer.  When it is default constructed, it has empty string.  You can freely copy or move around this struct, but
+ * never free its pointer.  str() function can be used to export the content as std::string. StringRef is adopted from
+ * <https://github.com/nghttp2/nghttp2/blob/29cbf8b83ff78faf405d1086b16adc09a8772eca/src/template.h#L376>
+ */
+class StringRef {
+ public:
+  using traits_type = std::char_traits<char>;
+  using value_type = traits_type::char_type;
+  using allocator_type = std::allocator<char>;
+  using size_type = std::allocator_traits<allocator_type>::size_type;
+  using difference_type = std::allocator_traits<allocator_type>::difference_type;
+  using const_reference = const value_type &;
+  using const_pointer = const value_type *;
+  using const_iterator = const_pointer;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  constexpr StringRef() : base_(""), len_(0) {}
+  explicit StringRef(const std::string &s) : base_(s.c_str()), len_(s.size()) {}
+  explicit StringRef(const char *s) : base_(s), len_(strlen(s)) {}
+  constexpr StringRef(const char *s, size_t n) : base_(s), len_(n) {}
+  template<typename CharT>
+  constexpr StringRef(const CharT *s, size_t n) : base_(reinterpret_cast<const char *>(s)), len_(n) {}
+  template<typename InputIt>
+  StringRef(InputIt first, InputIt last)
+      : base_(reinterpret_cast<const char *>(&*first)), len_(std::distance(first, last)) {}
+  template<typename InputIt>
+  StringRef(InputIt *first, InputIt *last)
+      : base_(reinterpret_cast<const char *>(first)), len_(std::distance(first, last)) {}
+  template<typename CharT, size_t N> constexpr static StringRef from_lit(const CharT (&s)[N]) {
+    return StringRef{s, N - 1};
+  }
+  static StringRef from_maybe_nullptr(const char *s) {
+    if (s == nullptr) {
+      return StringRef();
+    }
+
+    return StringRef(s);
+  }
+
+  constexpr const_iterator begin() const { return base_; };
+  constexpr const_iterator cbegin() const { return base_; };
+
+  constexpr const_iterator end() const { return base_ + len_; };
+  constexpr const_iterator cend() const { return base_ + len_; };
+
+  const_reverse_iterator rbegin() const { return const_reverse_iterator{base_ + len_}; }
+  const_reverse_iterator crbegin() const { return const_reverse_iterator{base_ + len_}; }
+
+  const_reverse_iterator rend() const { return const_reverse_iterator{base_}; }
+  const_reverse_iterator crend() const { return const_reverse_iterator{base_}; }
+
+  constexpr const char *c_str() const { return base_; }
+  constexpr size_type size() const { return len_; }
+  constexpr bool empty() const { return len_ == 0; }
+  constexpr const_reference operator[](size_type pos) const { return *(base_ + pos); }
+
+  std::string str() const { return std::string(base_, len_); }
+  const uint8_t *byte() const { return reinterpret_cast<const uint8_t *>(base_); }
+
+  operator std::string() const { return str(); }
+
+ private:
+  const char *base_;
+  size_type len_;
+};
+
+inline bool operator==(const StringRef &lhs, const StringRef &rhs) {
+  return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
+}
+
+inline bool operator==(const StringRef &lhs, const std::string &rhs) {
+  return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
+}
+
+inline bool operator==(const std::string &lhs, const StringRef &rhs) { return rhs == lhs; }
+
+inline bool operator==(const StringRef &lhs, const char *rhs) {
+  return lhs.size() == strlen(rhs) && std::equal(std::begin(lhs), std::end(lhs), rhs);
+}
+
+inline bool operator==(const char *lhs, const StringRef &rhs) { return rhs == lhs; }
+
+inline bool operator!=(const StringRef &lhs, const StringRef &rhs) { return !(lhs == rhs); }
+
+inline bool operator!=(const StringRef &lhs, const std::string &rhs) { return !(lhs == rhs); }
+
+inline bool operator!=(const std::string &lhs, const StringRef &rhs) { return !(rhs == lhs); }
+
+inline bool operator!=(const StringRef &lhs, const char *rhs) { return !(lhs == rhs); }
+
+inline bool operator!=(const char *lhs, const StringRef &rhs) { return !(rhs == lhs); }
+
+inline bool operator<(const StringRef &lhs, const StringRef &rhs) {
+  return std::lexicographical_compare(std::begin(lhs), std::end(lhs), std::begin(rhs), std::end(rhs));
+}
+
+inline std::string &operator+=(std::string &lhs, const StringRef &rhs) {
+  lhs.append(rhs.c_str(), rhs.size());
+  return lhs;
+}
+
+inline std::string operator+(const char *lhs, const StringRef &rhs) {
+  auto str = std::string(lhs);
+  str.append(rhs.c_str(), rhs.size());
+  return str;
+}
+
+inline std::string operator+(const StringRef &lhs, const char *rhs) {
+  auto str = lhs.str();
+  str.append(rhs);
+  return str;
+}
+
+#ifdef USE_JSON
+// NOLINTNEXTLINE(readability-identifier-naming)
+void convertToJson(const StringRef &src, JsonVariant dst);
+#endif  // USE_JSON
+
+}  // namespace esphome