diff --git a/.drone.yml b/.drone.yml
index 0d832f6d3ba63954d9d6efd27c22237f9dd6df47..7621a466dda7d6614039cbfec4e0472157c165ca 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -43,6 +43,7 @@ pipeline:
     volumes:
       - /cache:/cache
     commands:
+      - composer self-update --1
       - composer install --ansi --no-suggest --no-progress
 
   composer-update-lowest:
@@ -51,6 +52,7 @@ pipeline:
     volumes:
       - /cache:/cache
     commands:
+      - composer self-update --1
       - composer update --prefer-lowest --ansi --no-suggest --no-progress
     when:
       event:
@@ -105,4 +107,4 @@ matrix:
     - lowest
     - highest
   PHP_VERSION:
-    - 7.3
+    - 7.4
diff --git a/composer.json b/composer.json
index 2f2f955525757a42862a62f9dca496110206915e..52522f0b2a5b9c4941085aa990ee338cae50eb6e 100644
--- a/composer.json
+++ b/composer.json
@@ -6,29 +6,34 @@
     "minimum-stability": "dev",
     "prefer-stable": true,
     "require": {
-        "php": ">=7.3",
+        "php": ">=7.4",
         "cweagans/composer-patches": "^1.7",
-        "drupal/core": "^8.9 || ^9.1",
+        "drupal/core": "^8.9 || ^9.2",
         "openeuropa/oe_bootstrap_theme": "0.1.202202072010"
     },
     "require-dev": {
         "composer/installers": "^1.11",
-        "drupal/core-composer-scaffold": "^8.9 || ^9.1",
-        "drupal/config_devel": "~1.2",
         "drupal/better_exposed_filters": "^5.0",
+        "drupal/config_devel": "~1.2",
+        "drupal/core-composer-scaffold": "^8.9 || ^9.2",
+        "drupal/ctools": "^3.7",
         "drupal/drupal-extension": "~4.1",
+        "drupal/file_link": "^2.0.4",
+        "drupal/pathauto": "^1.8",
+        "drupal/search_api": "^1.21",
+        "drupal/search_api_autocomplete": "^1.5",
         "drush/drush": "^10.3",
-        "openeuropa/code-review": "1.7",
+        "easyrdf/easyrdf": "1.0.0 as 0.9.1",
+        "egulias/email-validator": "^2.1.22 || ^3.0",
+        "openeuropa/code-review": "^1.7 || ^2.0",
         "openeuropa/composer-artifacts": "~0.1",
-        "openeuropa/drupal-core-require-dev": "^8.9 || ^9.1",
+        "openeuropa/drupal-core-require-dev": "^8.9 || ^9.2",
         "openeuropa/oe_authentication": "^1.4",
         "openeuropa/oe_corporate_blocks": "^4.4",
         "openeuropa/oe_multilingual": "^1.9",
+        "openeuropa/oe_starter_content": "1.x-dev",
         "openeuropa/task-runner-drupal-project-symlink": "^1.0",
-        "phpspec/prophecy-phpunit": "^1 || ^2",
-        "easyrdf/easyrdf": "1.0.0 as 0.9.1",
-        "drupal/search_api": "^1.21",
-        "drupal/search_api_autocomplete": "^1.5"
+        "phpspec/prophecy-phpunit": "^1 || ^2"
     },
     "scripts": {
         "post-install-cmd": "./vendor/bin/run drupal:site-setup",
@@ -38,6 +43,10 @@
         "drupal":{
             "type": "composer",
             "url": "https://packages.drupal.org/8"
+        },
+        "openeuropa/oe_starter_content": {
+            "type": "git",
+            "url": "https://github.com/openeuropa/oe_starter_content"
         }
     },
     "extra": {
@@ -56,6 +65,12 @@
                 "web-root": "./build"
             }
         },
+        "_readme": [
+            "Explicit minimum version requirement of drupal/ctools module due to D9.2 compatability.",
+            "Explicit requirement for drupal/file_link due to https://www.drupal.org/project/file_link/issues/3147517. It can be removed when oe_media requires version 2.0.4 or above.",
+            "Explicit requirement for drupal/pathauto due to D9.2 compatability according to https://www.drupal.org/node/2979476.",
+            "Explicit requirement for egulias/email-validator due to https://www.drupal.org/project/drupal/issues/3061074#comment-14300579. It can be removed when Drupal core 9.2 support is droppped."
+        ],
         "installer-paths": {
             "build/core": [
                 "type:drupal-core"
diff --git a/docker-compose.yml b/docker-compose.yml
index 053e590a5d3a650befc9d2db5258bb32ffa54ae2..71e0089fbf3a4ed409399c44500b63631a36c894 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,7 +1,7 @@
 version: '2'
 services:
   web:
-    image: fpfis/httpd-php-dev:7.3
+    image: fpfis/httpd-php-dev:7.4
     working_dir: /var/www/html
     ports:
       - 8080:8080
diff --git a/modules/oe_whitelabel_news/README.md b/modules/oe_whitelabel_news/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8affd80eaed4d22f800f1d246265e03fa64e004c
--- /dev/null
+++ b/modules/oe_whitelabel_news/README.md
@@ -0,0 +1 @@
+# OpenEuropa Whitelabel News
\ No newline at end of file
diff --git a/modules/oe_whitelabel_news/config/install/core.date_format.oe_whitelabel_news_date.yml b/modules/oe_whitelabel_news/config/install/core.date_format.oe_whitelabel_news_date.yml
new file mode 100644
index 0000000000000000000000000000000000000000..373d4583e625c58c1069b48cd9a7e05a1aed54d2
--- /dev/null
+++ b/modules/oe_whitelabel_news/config/install/core.date_format.oe_whitelabel_news_date.yml
@@ -0,0 +1,7 @@
+langcode: en
+status: true
+dependencies: {  }
+id: oe_whitelabel_news_date
+label: 'OE Whitelabel News date'
+locked: false
+pattern: 'd F Y'
diff --git a/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.full.yml b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.full.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c7bd5898a2850b54fd0877192a1368ad13bf8cc2
--- /dev/null
+++ b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.full.yml
@@ -0,0 +1,59 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.node.oe_news.body
+    - field.field.node.oe_news.oe_featured_media
+    - field.field.node.oe_news.oe_publication_date
+    - field.field.node.oe_news.oe_summary
+    - node.type.oe_news
+  module:
+    - datetime
+    - text
+    - user
+
+id: node.oe_news.full
+targetEntityType: node
+bundle: oe_news
+mode: full
+content:
+  body:
+    type: text_default
+    label: hidden
+    settings: {  }
+    third_party_settings: {  }
+    weight: 12
+    region: content
+  links:
+    settings: {  }
+    third_party_settings: {  }
+    weight: 100
+    region: content
+  oe_featured_media:
+    type: entity_reference_entity_view
+    label: hidden
+    settings:
+      view_mode: default
+      link: false
+    third_party_settings: {  }
+    weight: 10
+    region: content
+  oe_publication_date:
+    type: datetime_default
+    label: hidden
+    settings:
+      timezone_override: ''
+      format_type: oe_whitelabel_news_date
+    third_party_settings: {  }
+    weight: 13
+    region: content
+  oe_summary:
+    type: basic_string
+    label: hidden
+    settings: {  }
+    third_party_settings: {  }
+    weight: 11
+    region: content
+hidden:
+  langcode: true
+  search_api_excerpt: true
diff --git a/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.teaser.yml b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.teaser.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b385718c7d92f746abfb503105c28ca6180bad7e
--- /dev/null
+++ b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.teaser.yml
@@ -0,0 +1,60 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.node.teaser
+    - field.field.node.oe_news.body
+    - field.field.node.oe_news.oe_featured_media
+    - field.field.node.oe_news.oe_publication_date
+    - field.field.node.oe_news.oe_summary
+    - node.type.oe_news
+  module:
+    - datetime
+    - text
+    - user
+
+id: node.oe_news.teaser
+targetEntityType: node
+bundle: oe_news
+mode: teaser
+content:
+  body:
+    type: text_default
+    label: hidden
+    settings: {  }
+    third_party_settings: {  }
+    weight: 12
+    region: content
+  links:
+    settings: {  }
+    third_party_settings: {  }
+    weight: 100
+    region: content
+  oe_featured_media:
+    type: entity_reference_entity_view
+    label: hidden
+    settings:
+      view_mode: default
+      link: false
+    third_party_settings: {  }
+    weight: 10
+    region: content
+  oe_publication_date:
+    type: datetime_default
+    label: hidden
+    settings:
+      timezone_override: ''
+      format_type: oe_whitelabel_news_date
+    third_party_settings: {  }
+    weight: 13
+    region: content
+  oe_summary:
+    type: basic_string
+    label: hidden
+    settings: {  }
+    third_party_settings: {  }
+    weight: 11
+    region: content
+hidden:
+  langcode: true
+  search_api_excerpt: true
diff --git a/modules/oe_whitelabel_news/oe_whitelabel_news.info.yml b/modules/oe_whitelabel_news/oe_whitelabel_news.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ba0c28c9cd82fa4fa348591b485216cc587e5a17
--- /dev/null
+++ b/modules/oe_whitelabel_news/oe_whitelabel_news.info.yml
@@ -0,0 +1,14 @@
+name: OpenEuropa Whitelabel News
+type: module
+description: Companion module to OE News providing styling to nodes.
+package: OpenEuropa Whitelabel Theme
+core_version_requirement: ^8.9 || ^9.2
+dependencies:
+  - oe_whitelabel:oe_whitelabel_helper
+  - oe_starter_content:oe_starter_content_news
+
+config_devel:
+  install:
+    - core.date_format.oel_whitelabel_news_date.yml
+    - core.entity_view_display.node.oe_news.full.yml
+    - core.entity_view_display.node.oe_news.teaser.yml
diff --git a/modules/oe_whitelabel_news/oe_whitelabel_news.module b/modules/oe_whitelabel_news/oe_whitelabel_news.module
new file mode 100644
index 0000000000000000000000000000000000000000..fc89a07f215d9c95d1c225cd5cad86f3cab484cf
--- /dev/null
+++ b/modules/oe_whitelabel_news/oe_whitelabel_news.module
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * OE Whitelabel theme News.
+ */
+
+declare(strict_types =  1);
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\media\MediaInterface;
+use Drupal\media\Plugin\media\Source\Image;
+use Drupal\media_avportal\Plugin\media\Source\MediaAvPortalPhotoSource;
+use Drupal\oe_bootstrap_theme\ValueObject\ImageValueObject;
+
+/**
+ * Implements template_preprocess_node() for the News node type.
+ */
+function oe_whitelabel_news_preprocess_node__oe_news(&$variables) {
+  if ($variables['view_mode'] !== 'full' && $variables['view_mode'] !== 'teaser') {
+    return;
+  }
+
+  /** @var \Drupal\node\NodeInterface $node */
+  $node = $variables['node'];
+
+  // Bail out if there is no media present.
+  if ($node->get('oe_featured_media')->isEmpty()) {
+    return;
+  }
+
+  /** @var \Drupal\media\Entity\Media $media */
+  $media = $node->get('oe_featured_media')->entity;
+  if (!$media instanceof MediaInterface) {
+    // The media entity is not available anymore, bail out.
+    return;
+  }
+
+  // Retrieve the correct media translation.
+  /** @var \Drupal\media\Entity\Media $media */
+  $media = \Drupal::service('entity.repository')->getTranslationFromContext($media, $node->language()->getId());
+
+  // Caches are handled by the formatter usually. Since we are not rendering
+  // the original render arrays, we need to propagate our caches to the
+  // paragraph template.
+  $cacheability = CacheableMetadata::createFromRenderArray($variables);
+  $cacheability->addCacheableDependency($media);
+
+  // Run access checks on the media entity.
+  $access = $media->access('view', $variables['user'], TRUE);
+  $cacheability->addCacheableDependency($access);
+  if (!$access->isAllowed()) {
+    $cacheability->applyTo($variables);
+    return;
+  }
+
+  // Get the media source.
+  $source = $media->getSource();
+
+  $is_image = $source instanceof MediaAvPortalPhotoSource || $source instanceof Image;
+
+  // If it's not an image and not a video, bail out.
+  if (!$is_image) {
+    $cacheability->applyTo($variables);
+    return;
+  }
+
+  $thumbnail = $media->get('thumbnail')->first();
+  $variables['image'] = ImageValueObject::fromImageItem($thumbnail);
+
+  if ($variables['view_mode'] == 'teaser') {
+    $variables['image'] = ['#markup' => $variables['image']->getSource()];
+  }
+
+  $cacheability->applyTo($variables);
+}
diff --git a/modules/oe_whitelabel_news/templates/field--node--oe-publication-date--oe-news.html.twig b/modules/oe_whitelabel_news/templates/field--node--oe-publication-date--oe-news.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..c1d5573167b19e22028950682f7880352c2e2654
--- /dev/null
+++ b/modules/oe_whitelabel_news/templates/field--node--oe-publication-date--oe-news.html.twig
@@ -0,0 +1,16 @@
+{#
+/**
+ * @file
+ * Theme override for field oe-publication-date.
+ */
+#}
+{% if label_hidden %}
+  {% for item in items %}
+    {{ item.content }}
+  {% endfor %}
+{% else %}
+  {{ label }}
+  {% for item in items %}
+    {{ item.content }}
+  {% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/modules/oe_whitelabel_news/templates/node--oe-news--full.html.twig b/modules/oe_whitelabel_news/templates/node--oe-news--full.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..26d8251d95a59f20b5add6a0d75c5a3c9d848d8d
--- /dev/null
+++ b/modules/oe_whitelabel_news/templates/node--oe-news--full.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * News full display.
+ */
+#}
+<article{{attributes}}>
+  {{ pattern('content_banner', {
+    background: 'gray',
+    title: label,
+    content: content.oe_summary,
+    image: image,
+    meta: [
+      content.oe_publication_date,
+    ]
+  }) }}
+  
+  <div class="container mt-md-4-75 mt-4">
+    <div class="row">
+      <div class="col-12 col-lg-10 col-xl-9 col-xxl-8 mb-4">
+        {{ content.body }}
+      </div>
+    </div>
+  </div>
+</article>
diff --git a/modules/oe_whitelabel_news/templates/node--oe-news--teaser.html.twig b/modules/oe_whitelabel_news/templates/node--oe-news--teaser.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..51cb95a61f580d497f9630740b58843e7fcca018
--- /dev/null
+++ b/modules/oe_whitelabel_news/templates/node--oe-news--teaser.html.twig
@@ -0,0 +1,21 @@
+{#
+/**
+ * @file
+ * Search result template.
+ */
+#}
+{% set _title %}
+  <a class="text-underline-hover" href="{{ url }}">{{ label }}</a>
+{% endset %}
+{% set _content %}
+  <span class="text-muted text-nowrap me-4-5">{{ content.oe_publication_date }}</span>
+{% endset %}
+<article{{attributes}}>
+  {{ pattern('card', {
+    variant: 'search',
+    title: _title,
+    text: content.oe_summary,
+    image: image,
+    content: _content
+  }) }}
+</article>
diff --git a/tests/src/Functional/ContentNewsRenderTest.php b/tests/src/Functional/ContentNewsRenderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7681b1c751758b6ddf3cfaa19efd3e915131ee0a
--- /dev/null
+++ b/tests/src/Functional/ContentNewsRenderTest.php
@@ -0,0 +1,157 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\Tests\oe_whitelabel\Functional;
+
+use Symfony\Component\DomCrawler\Crawler;
+use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
+use Drupal\media\Entity\Media;
+use Drupal\file\Entity\File;
+use Drupal\Tests\TestFileCreationTrait;
+
+/**
+ * Tests that the News content type renders correctly.
+ */
+class ContentNewsRenderTest extends WhitelabelBrowserTestBase {
+
+  use MediaTypeCreationTrait;
+  use TestFileCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'oe_whitelabel_news',
+  ];
+
+  /**
+   * A node to be rendered in different display views.
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected $node;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    // Create a sample image media entity to be embedded.
+    File::create([
+      'uri' => $this->getTestFiles('image')[0]->uri,
+    ])->save();
+    $media_image = Media::create([
+      'bundle' => 'image',
+      'name' => 'Starter Image test',
+      'oe_media_image' => [
+        [
+          'target_id' => 1,
+          'alt' => 'Starter Image test alt',
+          'title' => 'Starter Image test title',
+        ],
+      ],
+    ]);
+    $media_image->save();
+
+    // Create a News node.
+    /** @var \Drupal\node\Entity\Node $node */
+    $node = \Drupal::entityTypeManager()
+      ->getStorage('node')
+      ->create([
+        'type' => 'oe_news',
+        'title' => 'Test news node',
+        'oe_summary' => 'http://www.example.org is a web page',
+        'body' => 'News body',
+        'oe_publication_date' => [
+          'value' => '2022-02-09T20:00:00',
+        ],
+        'uid' => 1,
+        'status' => 1,
+      ]);
+    $node->set('oe_featured_media', [$media_image]);
+    $node->save();
+    $this->node = $node;
+  }
+
+  /**
+   * Tests that the News page renders correctly in full display.
+   */
+  public function testNewsRenderingFull(): void {
+    // Build node full view.
+    $builder = \Drupal::entityTypeManager()->getViewBuilder('node');
+    $build = $builder->view($this->node, 'full');
+    $render = $this->container->get('renderer')->renderRoot($build);
+    $crawler = new Crawler((string) $render);
+
+    // Assert content banner title.
+    $content_banner = $crawler->filter('.bcl-content-banner');
+    $this->assertEquals(
+      'Test news node',
+      trim($content_banner->filter('.card-title')->text())
+    );
+    // Assert content banner image.
+    $image = $content_banner->filter('img');
+    $this->assertCount(1, $image);
+    $this->assertCount(1, $image->filter('.card-img-top'));
+    $this->assertStringContainsString(
+      'image-test.png',
+      trim($image->attr('src'))
+    );
+    $this->assertEquals('Starter Image test alt',
+      $image->attr('alt')
+    );
+    // Assert content banner publication date.
+    $this->assertEquals(
+      '10 February 2022',
+      trim($content_banner->filter('.card-body > div.my-4')->text())
+    );
+    // Assert content banner summary.
+    $this->assertEquals(
+      'http://www.example.org is a web page',
+      trim($content_banner->filter('.oe-news__oe-summary')->text())
+    );
+    // Assert the news content.
+    $this->assertEquals(
+      'News body',
+      trim($crawler->filter('.oe-news__body')->text())
+    );
+  }
+
+  /**
+   * Tests that the News page renders correctly in teaser display.
+   */
+  public function testNewsRenderingTeaser(): void {
+    // Build node teaser view.
+    $builder = \Drupal::entityTypeManager()->getViewBuilder('node');
+    $build = $builder->view($this->node, 'teaser');
+    $render = $this->container->get('renderer')->renderRoot($build);
+    $crawler = new Crawler((string) $render);
+
+    // Assert content banner title.
+    $this->assertEquals(
+      'Test news node',
+      trim($crawler->filter('h5.card-title')->text())
+    );
+    // Assert content banner image.
+    $image = $crawler->filter('img');
+    $this->assertCount(1, $image);
+    $this->assertCount(1, $image->filter('.card-img-top'));
+    $this->assertStringContainsString(
+      'image-test.png',
+      trim($image->attr('src'))
+    );
+    // Assert content banner content.
+    $this->assertEquals(
+      'http://www.example.org is a web page',
+      trim($crawler->filter('p.card-text')->text())
+    );
+    // Assert content banner publication date.
+    $this->assertEquals(
+      '10 February 2022',
+      trim($crawler->filter('div.card-body > span.text-muted')->text())
+    );
+  }
+
+}
diff --git a/tests/src/Functional/WhitelabelBrowserTestBase.php b/tests/src/Functional/WhitelabelBrowserTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..7aae37c3cffb7b8ac893d6a143b962312db4472d
--- /dev/null
+++ b/tests/src/Functional/WhitelabelBrowserTestBase.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\Tests\oe_whitelabel\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Base class for testing content types.
+ */
+abstract class WhitelabelBrowserTestBase extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    // Enable and set OpenEuropa Theme as default.
+    \Drupal::service('theme_installer')->install(['oe_whitelabel']);
+    \Drupal::configFactory()
+      ->getEditable('system.theme')
+      ->set('default', 'oe_whitelabel')
+      ->save();
+
+    // Rebuild the ui_pattern definitions to collect the ones provided by
+    // oe_whitelabel itself.
+    \Drupal::service('plugin.manager.ui_patterns')->clearCachedDefinitions();
+  }
+
+}