diff --git a/.idea/$CACHE_FILE$ b/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..6cb8985
--- /dev/null
+++ b/.idea/$CACHE_FILE$
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="NodePackageJsonFileManager">
+    <packageJsonPaths />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/gatsby-drupal-module.iml b/.idea/gatsby-drupal-module.iml
new file mode 100644
index 0000000..c956989
--- /dev/null
+++ b/.idea/gatsby-drupal-module.iml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..28a804d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..3d20c7b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/gatsby-drupal-module.iml" filepath="$PROJECT_DIR$/.idea/gatsby-drupal-module.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/README.txt b/README.txt
index 5698997..c50177c 100644
--- a/README.txt
+++ b/README.txt
@@ -32,3 +32,9 @@ Now you're all set up to use preview! Make a change to your content,
 press save (keystroke by keystroke updates are not available yet), and watch as
 Gatsby updates before Drupal even notifies you that your changes have been saved.
 
+# Menus
+To enable Gatsby menus you will need to install the module until the point that the issue in JSON:API Extras is resolver https://www.drupal.org/project/jsonapi_extras/issues/2982133.
+The menu functionality relies on the overriding the menu_link_content parent field to use our "Alias link" formatter to ensure
+
+To expose the menu_link_content endpoint in the JSON:API you will either need to enable the "Administer menus and menu items" permission or use the key_auth module with Gatsby to an account with that permission.
+
diff --git a/config/optional/jsonapi_extras.jsonapi_resource_config.menu_link_content--menu_link_content.yml b/config/optional/jsonapi_extras.jsonapi_resource_config.menu_link_content--menu_link_content.yml
new file mode 100644
index 0000000..ad1b8ee
--- /dev/null
+++ b/config/optional/jsonapi_extras.jsonapi_resource_config.menu_link_content--menu_link_content.yml
@@ -0,0 +1,148 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - menu_link_content
+id: menu_link_content--menu_link_content
+disabled: false
+path: menu_link_content/menu_link_content
+resourceType: menu_link_content--menu_link_content
+resourceFields:
+  id:
+    fieldName: id
+    publicName: id
+    enhancer:
+      id: ''
+    disabled: false
+  uuid:
+    fieldName: uuid
+    publicName: uuid
+    enhancer:
+      id: ''
+    disabled: false
+  revision_id:
+    fieldName: revision_id
+    publicName: revision_id
+    enhancer:
+      id: ''
+    disabled: false
+  langcode:
+    fieldName: langcode
+    publicName: langcode
+    enhancer:
+      id: ''
+    disabled: false
+  bundle:
+    fieldName: bundle
+    publicName: bundle
+    enhancer:
+      id: ''
+    disabled: false
+  revision_created:
+    fieldName: revision_created
+    publicName: revision_created
+    enhancer:
+      id: ''
+    disabled: false
+  revision_user:
+    fieldName: revision_user
+    publicName: revision_user
+    enhancer:
+      id: ''
+    disabled: false
+  revision_log_message:
+    fieldName: revision_log_message
+    publicName: revision_log_message
+    enhancer:
+      id: ''
+    disabled: false
+  enabled:
+    fieldName: enabled
+    publicName: enabled
+    enhancer:
+      id: ''
+    disabled: false
+  title:
+    fieldName: title
+    publicName: title
+    enhancer:
+      id: ''
+    disabled: false
+  description:
+    fieldName: description
+    publicName: description
+    enhancer:
+      id: ''
+    disabled: false
+  menu_name:
+    fieldName: menu_name
+    publicName: menu_name
+    enhancer:
+      id: ''
+    disabled: false
+  link:
+    fieldName: link
+    publicName: link
+    enhancer:
+      id: alias_link
+    disabled: false
+  external:
+    fieldName: external
+    publicName: external
+    enhancer:
+      id: ''
+    disabled: false
+  rediscover:
+    fieldName: rediscover
+    publicName: rediscover
+    enhancer:
+      id: ''
+    disabled: false
+  weight:
+    fieldName: weight
+    publicName: weight
+    enhancer:
+      id: ''
+    disabled: false
+  expanded:
+    fieldName: expanded
+    publicName: expanded
+    enhancer:
+      id: ''
+    disabled: false
+  parent:
+    fieldName: parent
+    publicName: parent
+    enhancer:
+      id: ''
+    disabled: false
+  changed:
+    fieldName: changed
+    publicName: changed
+    enhancer:
+      id: ''
+    disabled: false
+  default_langcode:
+    fieldName: default_langcode
+    publicName: default_langcode
+    enhancer:
+      id: ''
+    disabled: false
+  revision_default:
+    fieldName: revision_default
+    publicName: revision_default
+    enhancer:
+      id: ''
+    disabled: false
+  revision_translation_affected:
+    fieldName: revision_translation_affected
+    publicName: revision_translation_affected
+    enhancer:
+      id: ''
+    disabled: false
+  metatag:
+    fieldName: metatag
+    publicName: metatag
+    enhancer:
+      id: ''
+    disabled: false
diff --git a/src/Plugin/jsonapi/FieldEnhancer/AliasLinkEnhancer.php b/src/Plugin/jsonapi/FieldEnhancer/AliasLinkEnhancer.php
new file mode 100644
index 0000000..a06a375
--- /dev/null
+++ b/src/Plugin/jsonapi/FieldEnhancer/AliasLinkEnhancer.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Drupal\gatsby\Plugin\jsonapi\FieldEnhancer;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerBase;
+use Shaper\Util\Context;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Use UUID for internal link field value.
+ *
+ * @ResourceFieldEnhancer(
+ *   id = "alias_link",
+ *   label = @Translation("Alias for link (link field only)"),
+ *   description = @Translation("Use alias for internal link field.")
+ * )
+ */
+class AliasLinkEnhancer extends ResourceFieldEnhancerBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doUndoTransform($data, Context $context) {
+    if (isset($data['uri'])) {
+      // Check if it is a link to an entity.
+      preg_match("/entity:(.*)\/(.*)/", $data['uri'], $parsed_uri);
+      if (!empty($parsed_uri)) {
+        $entity_type = $parsed_uri[1];
+        $entity_id = $parsed_uri[2];
+        $entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
+        if (!is_null($entity)) {
+          $data['uri_uuid'] = 'entity:' . $entity_type . '/' . $entity->bundle() . '/' . $entity->uuid();
+          $data['uri_alias'] = \Drupal::service('path.alias_manager')->getAliasByPath('/node/'.$entity->id());
+        }
+        // Remove the value.
+        else {
+          $data = [
+            'uri' => '',
+            'uri_uuid' => '',
+            'uri_alias' => '',
+            'title' => '',
+            'options' => [],
+          ];
+        }
+      }
+    }
+    return $data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doTransform($value, Context $context) {
+    if (isset($value['uri'])) {
+      // Check if it is a link to an entity.
+      preg_match("/entity:(.*)\/(.*)\/(.*)/", $value['uri'], $parsed_uri);
+      if (!empty($parsed_uri)) {
+        $entity_type = $parsed_uri[1];
+        $entity_uuid = $parsed_uri[3];
+        $entities = $this->entityTypeManager->getStorage($entity_type)->loadByProperties(['uuid' => $entity_uuid]);
+        if (!empty($entities)) {
+          $entity = array_shift($entities);
+          $value['uri_uuid'] = 'entity:' . $entity_type . '/' . $entity->id();
+          $value['uri_alias'] = \Drupal::service('path.alias_manager')->getAliasByPath('/node/'.$entity->id());
+        }
+        else {
+          // If the entity has not been imported yet we unset the field value.
+          $value = [];
+        }
+      }
+    }
+
+    return $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOutputJsonSchema() {
+    return [
+      'type' => 'object',
+    ];
+  }
+
+}
\ No newline at end of file
