JSFiddle - React, Tailwind, and code Playground

HTML

<div id="text"></div>
  
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nam rerum omnis, sunt, fugit optio, mollitia eos explicabo ex modi exercitationem iste earum! Quisquam ex ut, velit dolores culpa omnis sunt!</p>
  <img src="https://bipbap.ru/wp-content/uploads/2017/04/priroda_kartinki_foto_03.jpg" data-text="подпись 1" class="image">
<img src="https://st4.depositphotos.com/2012555/37952/i/1600/depositphotos_379525432-stock-photo-vintage-photo-butterfly-tree-flowers.jpg" data-text="подпись 2" class="image">

<ul>
  <li class="image">11111</li>
  <li class="image">222</li>
</ul>


<div class="circle-cursor circle-cursor--inner"></div>
<div class="circle-cursor circle-cursor--outer"></div>

CSS

html {
  cursor: none;
}

body {  
  margin: 0;
  --cursor-color: #000;  
}

.circle-cursor {
  position: fixed;
  left: 0;
  top: 0;
  pointer-events: none;
  border-radius: 50%;
  transition: background-color 100ms;
}

.circle-cursor--inner {
  width: 5px;
  height: 5px;
  left: -2.5px;
  top: -2.5px;
  z-index: 11000;
  background-color: var(--cursor-color);
}

.circle-cursor--inner.circle-cursor--hover {
  background-color: rgba(0,0,0,0);
}

.circle-cursor--outer {
  width: 30px;
  height: 30px;
  left: -15px;
  top: -15px;
  border: 1px solid var(--cursor-color);
  z-index: 12000;
  opacity: 0.2;
}

.circle-cursor--outer.circle-cursor--hover {
  background-color: var(--cursor-color);
}

img {
  max-width: 300px;
  margin-bottom: 55px;
}

li {
  margin-bottom: 20px;
}

#text {
  position: fixed;
  top: -9px;
  left: 0px;
  transition: opacity 0.3s;
  pointer-events: none;
}

JavaScript

const text = document.getElementById("text");
const cursorInner = document.querySelector('.circle-cursor--inner');
const cursorOuter = document.querySelector('.circle-cursor--outer');

let mx = window.innerWidth / 2;
let my = window.innerHeight / 2;
let cxi = mx,
  cyi = my,
  cxo = mx,
  cyo = my;

window.addEventListener('mousemove', (event) => {
  mx = event.clientX;
  my = event.clientY; 
});

function update() {
  const dxi = (mx - cxi) * 0.2;
  const dyi = (my - cyi) * 0.2;
  
  cxi += dxi;
  cyi += dyi;

  const dxo = (mx - cxo) * 0.07;
  const dyo = (my - cyo) * 0.07;
  cxo += dxo;
  cyo += dyo;

  cursorOuter.style.transform = `translate(${cxo}px, ${cyo}px)`;
  cursorInner.style.transform = `translate(${cxi}px, ${cyi}px)`;
  
  text.style.transform = `translate(${cxo}px, ${cyo}px)`;
  
  requestAnimationFrame(update);
}

requestAnimationFrame(update);

document.body.addEventListener('mouseover', (event) => {
	const image = event.target.closest('.image');
  if (!image) return;
  
  text.textContent = image.dataset.text;
  text.style.opacity = 1;
  
  cursorInner.classList.add('circle-cursor--hover');
  cursorOuter.classList.add('circle-cursor--hover');
});

document.body.addEventListener('mouseout', (event) => {
  const image = event.target.closest('.image');
  if (!image) return;
  
  text.style.opacity = 0;
  
  cursorInner.classList.remove('circle-cursor--hover');
  cursorOuter.classList.remove('circle-cursor--hover');
});