JSFiddle - React, Tailwind, and code Playground

HTML

<div class="timeline">
  <div class="event">
    <div class="event__body">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
    </div>
  </div>
  <div class="event">
    <div class="event__body">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
    </div>
  </div>
  <div class="event">
    <div class="event__body">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
    </div>
  </div>
  <div class="event">
    <div class="event__body">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
    </div>
  </div>
  <div class="event">
    <div class="event__body">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
    </div>
  </div>
</div>

CSS

/* line in center */
.timeline {
  position: relative;
  width: 100%;
}

.timeline:before {
  content: " ";
  display: block;
  position: absolute;
  top: 0;
  left: calc(50% - 2px);
  width: 4px;
  height: 100%;
  background: red;
}

/* events */
.event {
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
  width: 50%;
  height: auto;
  padding: 0 20px 0 0;
}
.event--right {
  /* Move padding to other side.
   * It is important that the padding does not change, because
   * changing the height of an element (which padding can do)
   * while layouting is not handled by the JavaScript. */
  padding: 0 0 0 20px;
}

/* discs on timeline */
.event:after {
  content: "";
  display: block;
  position: absolute;
  top: calc(50% - 5px);
  width: 0;
  height: 0;
  border-style: solid;
}
.event--left:after {
  right: 0;
  border-width: 10px 0 10px 20px;
  border-color: transparent transparent transparent #de2d26;
}
.event--right:after {
  left: 0;
  border-width: 10px 20px 10px 0;
  border-color: transparent #de2d26 transparent transparent;
}

/* event styling */
.event__body {
  padding: 20px;
  background: #de2d26;
  color: rgba(255, 255, 255, 0.9);
}

JavaScript

// wrap in anonymous function to not pollute global namespace
(function() {

  // find the minimium element in an array, and the index of it
  function min(arr) {
    var ind = -1, min = false, i;
    for (i = 0; i < arr.length; ++i) {
      if (min === false || arr[i] < min) {
        ind = i;
        min = arr[i];
      }
    }
    return {
      index: ind,
      value: min
    };
  }

  // position events within a single timeline container
  function updateTimeline(timeline, minSpace = 10) {
    var events = Array.from(timeline.querySelectorAll('.event')),
        tops = [0, 0],
        bottoms = [0, 0],
        minTops = [0, 0];
    // determine height of container, upper bound
    timeline.style.height = events.reduce(function(sum, event) {
      return sum + event.offsetHeight;
    }, 0) + 'px';

    // position events in timeline
    events.forEach(function(event) {
      // find highest point to put event at
      // first put it with its top aligned to the lowest bottom - this will
      // always yield a good solution, we check better ones later
      var h = event.offsetHeight,
          y = min(bottoms),
          x = (y.index === 0 ? 0 : 50),
          b = y.value + h,
          m = (tops[1 - y.index] + bottoms[1 - y.index]) / 2;
      event.className = 'event';
      event.classList.add('event--' + (y.index === 0 ? 'left' : 'right'));
      // try to squeeze element up as high as possible
      // first try to put midpoint of new event just below the midpoint of
      // the last placed event on the other side
      if (m + minSpace - h / 2 > minTops[y.index]) {
        y.value = m + minSpace - h / 2;
        b = y.value + h;
      }
      // it would be even better if the top of the new event could move
      // all the way up - this can be done if its midpoint is below the
      // midpoint of the last event on the other side
      if (minTops[y.index] + h / 2 > m + minSpace) {
        y.value = minTops[y.index];
        b = y.value + h;
     ...