diff --git a/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.stories.ts b/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.stories.ts
index 48d3870395eb38603593be3e9b551d56a4c7706a..08c2009885358aa98de04f572aecf923dd6ae7f7 100644
--- a/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.stories.ts
+++ b/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.stories.ts
@@ -1,7 +1,7 @@
 import type { Meta, StoryObj } from '@storybook/vue3';
 
 import OcDatasetFormStep1 from './OcDatasetFormStep1.vue';
-import type { OcOrganization, OcPerson, OcVocabulary } from '@/declarations';
+import type { OcOrganization, OcPerson, OcConcept } from '@/declarations';
 
 const meta: Meta<typeof OcDatasetFormStep1> = {
   component: OcDatasetFormStep1,
@@ -22,22 +22,22 @@ export const Default: Story = {
     modelValue: {},
     personSearchCallback: (query: string) => [{ '@id': `${query}-person-id`, '@type': ['http://xmlns.com/foaf/0.1/Person'], familyName: `${query} name`, givenName: `${query} given name` } as OcPerson],
     organizationSearchCallback: (query: string) => [{ '@id': `${query}-orga-id`, '@type': ['http://xmlns.com/foaf/0.1/Organization'], name: { en: `${query} name` } } as OcOrganization],
-    themeSearchCallback: (query: string) => [{ '@id': `${query}-id`, '@type': ['https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Concept'], prefLabel: { en: `${query} theme` } } as OcVocabulary],
-    typeSearchCallback: (query: string) => [{ '@id': `${query}-id`, '@type': ['https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Concept'], prefLabel: { en: `${query} type` } } as OcVocabulary],
+    themeVocabularies: ["http://publications.europa.eu/resource/authority/data-theme"],
+    typeVocabularies: ["http://publications.europa.eu/resource/authority/dataset-type"],
     frequencyList: [
       { '@id': `freq-1-id`, prefLabel: { en: `freq 1` } },
       { '@id': `freq-2-id`, prefLabel: { en: `freq 2` } },
-    ] as OcVocabulary[],
+    ] as OcConcept[],
     licenseList: [
       { '@id': `lic-1-id`, prefLabel: { en: `license 1` } },
       { '@id': `lic-2-id`, prefLabel: { en: `license 2` } },
-    ] as OcVocabulary[],
+    ] as OcConcept[],
     conformsToList: [
       { '@id': `conforms-1-id`, prefLabel: { en: `conforms 1` } },
       { '@id': `conforms-2-id`, prefLabel: { en: `conforms 2` } },
-    ] as OcVocabulary[],
-    languageSearchCallback: (query: string) => [{ '@id': `${query}-id`, '@type': ['https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Concept'], prefLabel: { en: `${query} lang` } } as OcVocabulary],
+    ] as OcConcept[],
+    languageVocabularies: ["http://publications.europa.eu/resource/authority/language"],
     keywordSearchCallback: (query: string) => [`${query} keyword 1`, `${query} keyword 2`],
-    spatialSearchCallback: (query: string) => [{ '@id': `${query}-id`, '@type': ['https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Concept'], prefLabel: { en: `${query} spatial` } } as OcVocabulary],
+    spatialVocabularies: ["http://publications.europa.eu/resource/authority/continent", "http://publications.europa.eu/resource/authority/country"],
   },
 };
diff --git a/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.vue b/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.vue
index 5c6a89075b7edcef12a608a07d024eee23ccb6be..23f349d4d6b53f42ab1ccaaacc7146a404e7134b 100644
--- a/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.vue
+++ b/src/components/DatasetMultiStep/OcDatasetFormStep1/OcDatasetFormStep1.vue
@@ -87,13 +87,14 @@
       <Message v-if="errorMessage" severity="error">{{ errorMessage }}</Message>
     </Field>
     <Field name="theme" v-slot="{ value, handleChange, errorMessage }">
-      <OcField for="theme" :metadata="formMetadata.theme">
-        <OcVocabularyAutocomplete
+      <OcField for="theme" :metadata="formMetadata.theme" :show-fair-badges="showFair">
+        <OcConceptAutocomplete
           inputId="theme"
           :model-value="value"
           @update:model-value="handleChange"
-          :vocabulary-search-callback="themeSearchCallback"
+          :vocabulariesUriList="themeVocabularies"
           :invalid="!!errorMessage"
+          with-browser
           required
           fluid
         />
@@ -115,14 +116,15 @@
       <Message v-if="errorMessage" severity="error">{{ errorMessage }}</Message>
     </Field>
     <Field name="type" v-slot="{ value, handleChange, errorMessage }">
-      <OcField for="type" :metadata="formMetadata.type">
-        <OcVocabularyAutocomplete
+      <OcField for="type" :metadata="formMetadata.type" :show-fair-badges="showFair">
+        <OcConceptAutocomplete
           inputId="type"
           :model-value="value"
           @update:model-value="handleChange"
-          :vocabulary-search-callback="typeSearchCallback"
+          :vocabulariesUriList="typeVocabularies"
           :invalid="!!errorMessage"
           :multiple="false"
+          with-browser
           required
           fluid
         />
@@ -130,12 +132,12 @@
       <Message v-if="errorMessage" severity="error">{{ errorMessage }}</Message>
     </Field>
     <Field name="language" v-slot="{ value, errorMessage, handleChange }">
-      <OcField for="language" :metadata="formMetadata.language">
-        <OcVocabularyAutocomplete
+      <OcField for="language" :metadata="formMetadata.language" :show-fair-badges="showFair">
+        <OcConceptAutocomplete
           inputId="language"
           :model-value="value"
           @update:model-value="handleChange"
-          :vocabulary-search-callback="languageSearchCallback"
+          :vocabulariesUriList="languageVocabularies"
           :invalid="!!errorMessage"
           required
           fluid
@@ -149,7 +151,7 @@
           inputId="status"
           :model-value="value"
           @update:model-value="handleChange"
-          :optionLabel="(item: OcVocabulary) => translateValue(item.prefLabel)"
+          :optionLabel="(item: OcConcept) => translateValue(item.prefLabel)"
           dataKey="@id"
           :options="statusList"
           :invalid="!!errorMessage"
@@ -174,14 +176,15 @@
       <Message v-if="errorMessage" severity="error">{{ errorMessage }}</Message>
     </Field>
     <Field name="spatial" v-slot="{ value, errorMessage, handleChange }" v-if="showFair">
-      <OcField for="spatial" :metadata="formMetadata.spatial">
-        <OcVocabularyAutocomplete
+      <OcField for="spatial" :metadata="formMetadata.spatial" :show-fair-badges="showFair">
+        <OcConceptAutocomplete
           inputId="spatial"
           :model-value="value"
           @update:model-value="handleChange"
-          :vocabulary-search-callback="spatialSearchCallback"
+          :vocabulariesUriList="spatialVocabularies"
           :invalid="!!errorMessage"
           :multiple="false"
+          with-browser
           required
           fluid
         />
@@ -194,7 +197,7 @@
           inputId="accrualPeriodicity "
           :model-value="value"
           @update:model-value="handleChange"
-          :optionLabel="(item: OcVocabulary) => translateValue(item.prefLabel)"
+          :optionLabel="(item: OcConcept) => translateValue(item.prefLabel)"
           dataKey="@id"
           :options="frequencyList"
           :invalid="!!errorMessage"
@@ -229,7 +232,7 @@
           inputId="license"
           :model-value="value"
           @update:model-value="handleChange"
-          :optionLabel="(item: OcVocabulary) => translateValue(item.prefLabel)"
+          :optionLabel="(item: OcConcept) => translateValue(item.prefLabel)"
           dataKey="@id"
           :options="licenseList"
           :invalid="!!errorMessage"
@@ -246,7 +249,7 @@
           inputId="conformsTo"
           :model-value="value"
           @update:model-value="handleChange"
-          :optionLabel="(item: OcVocabulary) => translateValue(item.prefLabel)"
+          :optionLabel="(item: OcConcept) => translateValue(item.prefLabel)"
           dataKey="@id"
           :options="conformsToList"
           :invalid="!!errorMessage"
@@ -278,7 +281,7 @@
 <script setup lang="ts">
 import { Field, Form, type GenericObject } from 'vee-validate'
 import * as yup from 'yup'
-import type { OcDataset, OcOrganization, OcPerson, OcVocabulary } from '@/declarations'
+import type { OcDataset, OcOrganization, OcPerson, OcConcept } from '@/declarations'
 import Button from 'primevue/button'
 import { useI18n } from 'vue-i18n'
 import DatePicker from 'primevue/datepicker'
@@ -287,7 +290,7 @@ import Textarea from 'primevue/textarea'
 import Message from 'primevue/message'
 import OcField from '@/components/FormInputs/OcField/OcField.vue'
 import OcAgentAutocomplete from '@/components/FormInputs/OcAgentAutocomplete/OcAgentAutocomplete.vue'
-import OcVocabularyAutocomplete from '@/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.vue'
+import OcConceptAutocomplete from '@/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.vue'
 import Select from 'primevue/select'
 import { useTranslateValue } from '@/composables/translateValue'
 import { datasetsMetadata } from '@/modelMetadata/datasets'
@@ -309,36 +312,36 @@ defineProps({
     type: Function as PropType<(query: string) => OcOrganization[]>,
     required: true
   },
-  themeSearchCallback: {
-    type: Function as PropType<(query: string) => OcVocabulary[]>,
+  themeVocabularies: {
+    type: Array<string>,
     required: true
   },
-  typeSearchCallback: {
-    type: Function as PropType<(query: string) => OcVocabulary[]>,
+  typeVocabularies: {
+    type: Array<string>,
     required: true
   },
   frequencyList: {
-    type: Array as PropType<OcVocabulary[]>,
+    type: Array as PropType<OcConcept[]>,
     required: true
   },
   statusList: {
-    type: Array as PropType<OcVocabulary[]>,
+    type: Array as PropType<OcConcept[]>,
     required: true
   },
-  languageSearchCallback: {
-    type: Function as PropType<(query: string) => OcVocabulary[]>,
+  languageVocabularies: {
+    type: Array<string>,
     required: true
   },
-  spatialSearchCallback: {
-    type: Function as PropType<(query: string) => OcVocabulary[]>,
+  spatialVocabularies: {
+    type: Array<string>,
     required: true
   },
   licenseList: {
-    type: Array as PropType<OcVocabulary[]>,
+    type: Array as PropType<OcConcept[]>,
     required: true
   },
   conformsToList: {
-    type: Array as PropType<OcVocabulary[]>,
+    type: Array as PropType<OcConcept[]>,
     required: true
   },
   keywordSearchCallback: {
@@ -376,36 +379,34 @@ const validationSchema = toTypedSchema(
       .object<OcOrganization>()
       .required()
       .label(translateValue(formMetadata.publisher.label)),
-    accrualPeriodicity: yup
-      .object<OcVocabulary>()
-      .required()
-      .label(translateValue(formMetadata.accrualPeriodicity.label)),
     contactPoint: yup
       .mixed<OcPerson | OcOrganization>()
       .required()
       .label(translateValue(formMetadata.contactPoint.label)),
-    type: yup.mixed<OcVocabulary>().required().label(translateValue(formMetadata.type.label)),
+    type: yup.mixed<OcConcept>().required().label(translateValue(formMetadata.type.label)),
     theme: yup
       .array()
-      .of(yup.object<OcVocabulary>())
-      .ensure()
+      .of(yup.object<OcConcept>())
       .min(1)
       .label(translateValue(formMetadata.theme.label)),
-    status: yup.mixed<OcVocabulary>().label(translateValue(formMetadata.status.label)),
+    accrualPeriodicity: yup
+      .object<OcConcept>()
+      .required()
+      .label(translateValue(formMetadata.accrualPeriodicity.label)),
+    status: yup.mixed<OcConcept>().label(translateValue(formMetadata.status.label)),
     issued: yup.date().required().label(translateValue(formMetadata.issued.label)),
     modified: yup.date().label(translateValue(formMetadata.modified.label)),
     language: yup
       .array()
-      .of(yup.object<OcVocabulary>())
-      .ensure()
+      .of(yup.object<OcConcept>())
       .min(1)
       .label(translateValue(formMetadata.language.label)),
     keyword: yup.array().of(yup.string()).label(translateValue(formMetadata.keyword.label)),
     temporal: yup.mixed<[Date, Date]>().label(translateValue(formMetadata.temporal.label)),
-    spatial: yup.mixed<OcVocabulary>().label(translateValue(formMetadata.spatial.label)),
+    spatial: yup.mixed<OcConcept>().label(translateValue(formMetadata.spatial.label)),
     landingPage: yup.string().url().label(translateValue(formMetadata.landingPage.label)),
-    license: yup.mixed<OcVocabulary>().label(translateValue(formMetadata.license.label)),
-    conformsTo: yup.mixed<OcVocabulary>().label(translateValue(formMetadata.conformsTo.label)),
+    license: yup.mixed<OcConcept>().label(translateValue(formMetadata.license.label)),
+    conformsTo: yup.mixed<OcConcept>().label(translateValue(formMetadata.conformsTo.label)),
     version: yup.string().label(translateValue(formMetadata.version.label))
   })
 )
diff --git a/src/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.stories.ts b/src/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c3232531bfbaf82b123871bc69f017fcafd858d3
--- /dev/null
+++ b/src/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.stories.ts
@@ -0,0 +1,51 @@
+import type { Meta, StoryObj } from '@storybook/vue3';
+
+import OcConceptAutocomplete from './OcConceptAutocomplete.vue';
+
+const meta: Meta<typeof OcConceptAutocomplete> = {
+  component: OcConceptAutocomplete,
+};
+
+export default meta;
+type Story = StoryObj<typeof OcConceptAutocomplete>;
+
+export const Default: Story = {
+  render: (args) => ({
+    components: { OcConceptAutocomplete },
+    setup() {
+      return { args };
+    },
+    template: '<OcConceptAutocomplete v-bind="args" />',
+  }),
+  args: {
+    vocabulariesUriList: ['https://data.archives-ouvertes.fr/subject', 'http://publications.europa.eu/resource/authority/country'],
+  },
+};
+
+export const NoneMultiple: Story = {
+  render: (args) => ({
+    components: { OcConceptAutocomplete },
+    setup() {
+      return { args };
+    },
+    template: '<OcConceptAutocomplete v-bind="args" />',
+  }),
+  args: {
+    vocabulariesUriList: ['https://data.archives-ouvertes.fr/subject', 'http://publications.europa.eu/resource/authority/country'],
+    multiple: false
+  },
+};
+
+export const WithBrowser: Story = {
+  render: (args) => ({
+    components: { OcConceptAutocomplete },
+    setup() {
+      return { args };
+    },
+    template: '<OcConceptAutocomplete v-bind="args" />',
+  }),
+  args: {
+    vocabulariesUriList: ['https://data.archives-ouvertes.fr/subject', 'http://publications.europa.eu/resource/authority/country'],
+    withBrowser: true
+  },
+};
\ No newline at end of file
diff --git a/src/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.vue b/src/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4482829bad719e6293b9eeb6852754b8262027c0
--- /dev/null
+++ b/src/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.vue
@@ -0,0 +1,106 @@
+<template>
+  <div class="flex flex-col w-full">
+    <div class="flex flex-row w-full gap-2">
+      <AutoComplete
+        :inputId="$attrs.inputId"
+        v-model="model"
+        :suggestions="items"
+        :optionLabel="displayName"
+        :multiple="multiple"
+        :forceSelection="forceSelection"
+        :typeahead="forceSelection"
+        :minLength="1"
+        @complete="search($event.query)"
+        :loading="loading"
+        :pt:pcInput:root:class="{ 'w-full': true, 'border-amber-200': !!errorMessage }"
+        class="w-full"
+      />
+      <Button
+        v-if="withBrowser"
+        class="p-button-secondary rounded-md whitespace-nowrap h-fit"
+        @click="isBrowserVisible = !isBrowserVisible"
+      >
+        {{ t('conceptAutocomplete.browseTerms') }}
+      </Button>
+      <Dialog
+        v-if="withBrowser"
+        v-model:visible="isBrowserVisible"
+        dismissable-mask
+        modal
+        class="max-w-[800px]"
+        :header="t('conceptAutocomplete.browseTerms')"
+      >
+        <OcConceptBrowser
+          v-model="model"
+          :vocabularies-uri-list="vocabulariesUriList"
+          :multiple="multiple"
+        />
+      </Dialog>
+    </div>
+    <Message class="mt-2" v-if="!!errorMessage" severity="warn">
+      {{ t('form.error.autocomplete') }}
+    </Message>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useTranslateValue } from '@/composables/translateValue'
+import type { OcConcept } from '@/declarations'
+import AutoComplete from 'primevue/autocomplete'
+import Message from 'primevue/message'
+import Button from 'primevue/button';
+import Dialog from 'primevue/dialog';
+import OcConceptBrowser from '@/components/FormInputs/OcConceptBrowser/OcConceptBrowser.vue'
+import { ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { queryVocabulariesList } from '@/sparql/vocabularies';
+
+const { t, locale } = useI18n()
+const { translateValue } = useTranslateValue()
+
+const props = defineProps({
+  vocabulariesUriList: {
+    type: Array<string>,
+    required: true
+  },
+  multiple: {
+    type: Boolean,
+    required: false,
+    default: true
+  },
+  forceSelection: {
+    type: Boolean,
+    required: false,
+    default: true
+  },
+  withBrowser: {
+    type: Boolean,
+    required: false,
+    default: false
+  }
+})
+const model = defineModel<OcConcept[] | undefined>()
+
+const loading = ref<boolean>(false)
+const errorMessage = ref<string | null>(null)
+const items = ref<OcConcept[]>([])
+
+const isBrowserVisible = ref(false)
+
+const displayName = (item: OcConcept) => translateValue(item.prefLabel)
+
+async function search(query: string) {
+  loading.value = true
+  errorMessage.value = null
+
+  try {
+    items.value = (await queryVocabulariesList(props.vocabulariesUriList, query, locale.value)).graph
+  } catch (e) {
+    items.value = []
+    errorMessage.value = (e as Error).message
+    console.error(e)
+  }
+
+  loading.value = false
+}
+</script>
diff --git a/src/components/FormInputs/OcConceptBrowser/OcConceptBrowser.stories.ts b/src/components/FormInputs/OcConceptBrowser/OcConceptBrowser.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7a0293de8d969c9d4e828467df7d6bbc60363055
--- /dev/null
+++ b/src/components/FormInputs/OcConceptBrowser/OcConceptBrowser.stories.ts
@@ -0,0 +1,23 @@
+import type { Meta, StoryObj } from '@storybook/vue3'
+
+import OcConceptBrowser from './OcConceptBrowser.vue'
+
+const meta: Meta<typeof OcConceptBrowser> = {
+  component: OcConceptBrowser
+}
+
+export default meta
+type Story = StoryObj<typeof OcConceptBrowser>
+
+export const Default: Story = {
+  render: (args) => ({
+    components: { OcConceptBrowser },
+    setup() {
+      return { args }
+    },
+    template: '<OcConceptBrowser v-bind="args" />'
+  }),
+  args: {
+    vocabulariesUriList: ['https://data.archives-ouvertes.fr/subject', 'http://publications.europa.eu/resource/authority/country'],
+  }
+}
\ No newline at end of file
diff --git a/src/components/FormInputs/OcConceptBrowser/OcConceptBrowser.vue b/src/components/FormInputs/OcConceptBrowser/OcConceptBrowser.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b4a1440c8a316f4f582288b9eaab7d010804328f
--- /dev/null
+++ b/src/components/FormInputs/OcConceptBrowser/OcConceptBrowser.vue
@@ -0,0 +1,174 @@
+<template>
+  <div>
+    <Select
+      v-if="vocabulariesListSelectOptions.length > 1"
+      v-model="selectedVocabulary"
+      :options="vocabulariesListSelectOptions"
+      optionLabel="name"
+      placeholder="Select a vocabulary"
+      class="w-full"
+    />
+    <Message class="mt-2" v-if="!!errorMessage" severity="warn">
+      {{ t('form.error.autocomplete') }}
+    </Message>
+    <Tree
+      v-if="!loading"
+      :value="nodes"
+      v-model:selectionKeys="selectedKeys"
+      @node-select="onNodeSelect"
+      @node-unselect="onNodeUnSelect"
+      @node-expand="onNodeExpend"
+      :selectionMode="multiple ? 'multiple' : 'single'"
+      :loading="loading"
+      loading-mode="icon"
+      class="w-full"
+    />
+    <ProgressSpinner v-if="loading" class="block m-auto"/>
+  </div>
+</template>
+
+<script setup lang="ts">
+import Tree from 'primevue/tree'
+import Select from 'primevue/select';
+import ProgressSpinner from 'primevue/progressspinner';
+import { useTranslateValue } from '@/composables/translateValue'
+import type { OcConcept, OcConceptScheme } from '@/declarations'
+import { computed, onBeforeMount, onMounted, ref, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { TreeNode } from 'primevue/treenode'
+import { getConceptChildren, getVocabulariesInformations, getVocabularyRootConcepts } from '@/sparql/vocabularies';
+
+const { t } = useI18n()
+const { translateValue } = useTranslateValue()
+
+const props = defineProps({
+  vocabulariesUriList: {
+    type: Array<string>,
+    required: true
+  },
+  multiple: {
+    type: Boolean,
+    required: false,
+    default: true
+  },
+})
+
+const model = defineModel<OcConcept[] | undefined>()
+const selectedKeys = ref()
+const nodes = ref<TreeNode[]>([])
+
+const selectedVocabulary = ref()
+const vocabulariesList = ref<OcConceptScheme[]>([])
+const vocabulariesListSelectOptions = computed(() => vocabulariesList.value.map((vocabulary) => ({ 'code': vocabulary['@id'], 'name': translateValue(vocabulary['prefLabel']) })))
+
+const loading = ref<boolean>(false)
+const errorMessage = ref<string | null>(null)
+
+onBeforeMount(async () => {
+  if (props.vocabulariesUriList.length){
+    loading.value = true
+    try {
+      vocabulariesList.value = (await getVocabulariesInformations(props.vocabulariesUriList)).graph
+    } catch (e) {
+      vocabulariesList.value = []
+      errorMessage.value = (e as Error).message
+      console.error(e)
+    }
+    loading.value = false
+  }
+  if (vocabulariesListSelectOptions.value.length == 1){
+    selectedVocabulary.value = vocabulariesListSelectOptions.value[0]
+  }
+})
+
+const onNodeSelect = (node: TreeNode) => {
+  if (props.multiple) {
+    if (model.value === undefined) {
+      model.value = [
+        {
+          '@id': node.key,
+          prefLabel: node.label
+        }
+      ]
+    } else {
+      model.value = [
+        ...model.value,
+        {
+          '@id': node.key,
+          prefLabel: node.label
+        }
+      ]
+    }
+  } else {
+    model.value = {
+      '@id': node.key,
+      prefLabel: node.label
+    }
+  }
+}
+
+const onNodeUnSelect = (node: TreeNode) => {
+  if (!model.value) {
+    model.value = []
+  }
+  model.value = model.value.filter((item) => item['@id'] != node.key)
+}
+
+const onNodeExpend = async (node: TreeNode) => {
+  if (!node.children && !node.leaf) {
+    node.loading = true
+    try {
+      node.children = (await getConceptChildren(selectedVocabulary.value.code, node.key)).graph.map(
+        (term) => OcConcept2TreeNode(term)
+      ).sort((a, b) => a.label >= b.label)
+    } catch (e) {
+      node.children = []
+      errorMessage.value = (e as Error).message
+      console.error(e)
+    }
+    node.loading = false
+  }
+}
+
+watch(model, () => {
+  if (Array.isArray(model.value)) {
+    selectedKeys.value = model.value?.reduce((a, v) => ({ ...a, [v['@id']]: true }), {})
+  } else if (model.value) {
+    selectedKeys.value = { [model.value['@id']]: true }
+  }
+})
+
+onMounted(() => {
+  if (Array.isArray(model.value)) {
+    selectedKeys.value = model.value?.reduce((a, v) => ({ ...a, [v['@id']]: true }), {})
+  } else if (model.value) {
+    selectedKeys.value = { [model.value['@id']]: true }
+  }
+})
+
+watch(selectedVocabulary, async () => {
+  if (selectedVocabulary.value) {
+    loading.value = true
+    try {
+      nodes.value = (await getVocabularyRootConcepts(selectedVocabulary.value.code)).graph.map(
+        (term) => OcConcept2TreeNode(term)
+      ).sort((a, b) => a.label >= b.label)
+    } catch (e) {
+      nodes.value = []
+      errorMessage.value = (e as Error).message
+      console.error(e)
+    }
+    
+    loading.value = false
+  }
+})
+
+const OcConcept2TreeNode = (term:OcConcept): TreeNode => {
+  return {
+    key: term['@id'],
+    label: translateValue(term['prefLabel']),
+    leaf: 'narrower' in term ? false : true,
+  }
+}
+
+</script>
diff --git a/src/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.stories.ts b/src/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.stories.ts
deleted file mode 100644
index 1a5abffae00053f46d0dddd8ecea44d6f1b1bb82..0000000000000000000000000000000000000000
--- a/src/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.stories.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/vue3';
-
-import OcVocabularyAutocomplete from './OcVocabularyAutocomplete.vue';
-import type { OcVocabulary } from '@/declarations';
-
-const meta: Meta<typeof OcVocabularyAutocomplete> = {
-  component: OcVocabularyAutocomplete,
-};
-
-export default meta;
-type Story = StoryObj<typeof OcVocabularyAutocomplete>;
-
-export const Default: Story = {
-  render: (args) => ({
-    components: { OcVocabularyAutocomplete },
-    setup() {
-      return { args };
-    },
-    template: '<OcVocabularyAutocomplete v-bind="args" />',
-  }),
-  args: {
-    vocabularySearchCallback: (query: string) => [{ '@id': `${query}-id`, '@type': ['https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Concept'], prefLabel: { en: `${query} vocab` } } as OcVocabulary],
-  },
-};
-
-export const NoneMultiple: Story = {
-  render: (args) => ({
-    components: { OcVocabularyAutocomplete },
-    setup() {
-      return { args };
-    },
-    template: '<OcVocabularyAutocomplete v-bind="args" />',
-  }),
-  args: {
-    vocabularySearchCallback: (query: string) => [{ '@id': `${query}-id`, '@type': ['https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Concept'], prefLabel: { en: `${query} vocab` } } as OcVocabulary],
-    multiple: false
-  },
-};
-
diff --git a/src/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.vue b/src/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.vue
deleted file mode 100644
index 74f4e5f9719cc569322c94f91e657dee2c1525f9..0000000000000000000000000000000000000000
--- a/src/components/FormInputs/OcVocabularyAutocomplete/OcVocabularyAutocomplete.vue
+++ /dev/null
@@ -1,72 +0,0 @@
-<template>
-  <div class="flex flex-col w-full">
-    <AutoComplete
-      :inputId="$attrs.inputId"
-      v-model="model"
-      :suggestions="items"
-      :optionLabel="displayName"
-      :multiple="multiple"
-      :forceSelection="forceSelection"
-      :typeahead="forceSelection"
-      :minLength="1"
-      @complete="search($event.query)"
-      :loading="loading"
-      :pt:pcInput:root:class="{ 'w-full': true, 'border-amber-200': !!errorMessage }"
-    />
-    <Message class="mt-2" v-if="!!errorMessage" severity="warn">
-      {{ t('form.error.autocomplete') }}
-    </Message>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useTranslateValue } from '@/composables/translateValue'
-import type { OcVocabulary } from '@/declarations'
-import AutoComplete from 'primevue/autocomplete'
-import Message from 'primevue/message'
-import type { PropType } from 'vue'
-import { ref } from 'vue'
-import { useI18n } from 'vue-i18n'
-
-const { t } = useI18n()
-const { translateValue } = useTranslateValue()
-
-const props = defineProps({
-  vocabularySearchCallback: {
-    type: Function as PropType<(query: string) => OcVocabulary[]>,
-    required: true
-  },
-  multiple: {
-    type: Boolean,
-    required: false,
-    default: true
-  },
-  forceSelection: {
-    type: Boolean,
-    required: false,
-    default: true
-  }
-})
-const model = defineModel<OcVocabulary[] | undefined>()
-
-const loading = ref<boolean>(false)
-const errorMessage = ref<string | null>(null)
-const items = ref<OcVocabulary[]>([])
-
-const displayName = (item: OcVocabulary) => translateValue(item.prefLabel)
-
-async function search(query: string) {
-  loading.value = true
-  errorMessage.value = null
-
-  try {
-    items.value = await props.vocabularySearchCallback(query)
-  } catch (e) {
-    items.value = []
-    errorMessage.value = (e as Error).message
-    console.error(e)
-  }
-
-  loading.value = false
-}
-</script>
diff --git a/src/declarations.ts b/src/declarations.ts
index 98504a0da2db27b949fc768a1ad23c708db1c9ee..1ee6e8a11a1990acb646d19da72dddedb18b4fbe 100644
--- a/src/declarations.ts
+++ b/src/declarations.ts
@@ -114,28 +114,34 @@ export type OcDataset = {
   creator?: Array<OcPerson | OcOrganization>,
   publisher?: OcOrganization,
   contactPoint?: OcPerson | OcOrganization,
-  type?: OcVocabulary,
-  theme?: OcVocabulary[],
-  accrualPeriodicity?: OcVocabulary,
+  type?: OcConcept,
+  theme?: OcConcept[],
+  accrualPeriodicity?: OcConcept,
   issued?: Date,
-  language?: OcVocabulary[],
+  language?: OcConcept[],
   keyword?: string[],
-  status?: OcVocabulary,
+  status?: OcConcept,
   modified?: Date,
   temporal?: [Date, Date],
   spatial?: any, // @todo .?
   landingPage?: string,
-  license?: OcVocabulary,
-  conformsTo?: OcVocabulary,
+  license?: OcConcept,
+  conformsTo?: OcConcept,
   version?: string,
   catalog?: OcCatalog,
 }
 
-export type OcVocabulary = {
+export type OcConceptScheme = {
   "@id": string
   prefLabel: LocalizedProperty
 }
 
+export type OcConcept = {
+  "@id": string
+  prefLabel: LocalizedProperty
+  narrower?: string[]
+}
+
 export type OcFieldMetadata = {
   label: LocalizedProperty,
   propertyUri?: string,
diff --git a/src/locales/en.ts b/src/locales/en.ts
index 254a66b45642094994ba3f2cc2df937f04a5715f..f2834d90e56f7447e6268cff35500dd1a86b969a 100644
--- a/src/locales/en.ts
+++ b/src/locales/en.ts
@@ -177,5 +177,11 @@ export default {
         },
       }
     }
+  },
+  conceptAutocomplete: {
+    search: 'Search for a term',
+    browseTerms: 'Browse terms',
+    vocabularies: 'Vocabularies',
+    selectedTerms: 'Selected terms',
   }
 }
diff --git a/src/locales/fr.ts b/src/locales/fr.ts
index c4bc5a61f45196945f6b0cf1fd5b30cc66c27c30..cee97fe59303e1daf4b0721098d482713f47c0b3 100644
--- a/src/locales/fr.ts
+++ b/src/locales/fr.ts
@@ -177,5 +177,11 @@ export default {
         },
       }
     }
+  },
+  conceptAutocomplete: {
+    search: 'Rechercher un terme',
+    browseTerms: 'Parcourir les termes',
+    vocabularies: 'Vocabulaires',
+    selectedTerms: 'Termes sélectionnés',
   }
 }
diff --git a/src/modelMetadata/datasets.ts b/src/modelMetadata/datasets.ts
index 5035ad3b7aa2205272ee68d56016089cf82821ff..8db1e7824e8a27164bdcd83b2f60d9f698f954dc 100644
--- a/src/modelMetadata/datasets.ts
+++ b/src/modelMetadata/datasets.ts
@@ -182,7 +182,7 @@ export const datasetsMetadata: OcModelMetadata<OcDataset> = {
       en: "The value SHOULD be taken from a well governed and broadly recognised controlled vocabulary.",
       fr: "La valeur DEVRAIT être tirée d'un vocabulaire contrôlé bien géré et largement reconnu.",
     },
-    vocabularies: ["urn:group:dct:type:dataset"],
+    vocabularies: ["http://publications.europa.eu/resource/authority/dataset-type"],
   },
   status: {
     label: {
@@ -248,7 +248,7 @@ export const datasetsMetadata: OcModelMetadata<OcDataset> = {
     propertyUri: "http://purl.org/dc/terms/spatial",
     dereferencement: "https://www.w3.org/TR/vocab-dcat-3/#Property:dataset_spatial",
     fair: ['f'],
-    vocabularies: ["urn:group:dct:spatial"],
+    vocabularies: ["http://publications.europa.eu/resource/authority/continent", "http://publications.europa.eu/resource/authority/country"],
     comment: {
       en: "The spatial coverage of a dataset may be encoded as an instance of dcterms:Location, or may be indicated using an IRI reference (link) to a resource describing a location. It is recommended that links are to entries in a well maintained gazetteer such as Geonames.",
       fr: "La couverture spatiale d'un jeu de données peut être encodée comme une instance de dcterms:Location, ou peut être indiquée en utilisant une référence IRI (lien) vers une ressource décrivant une localisation. Il est recommandé que les liens renvoient à des entrées d'un répertoire toponymique bien tenu, tel que Geonames.",
diff --git a/src/pages/community/[community].datasets/new.vue b/src/pages/community/[community].datasets/new.vue
index 7f8efe7a95e3b25ef14d6d5c58e4577a129de435..45c14d759bc853411744197e0f946694969ab748 100644
--- a/src/pages/community/[community].datasets/new.vue
+++ b/src/pages/community/[community].datasets/new.vue
@@ -61,7 +61,7 @@
 
 <script setup lang="ts">
 import OcLayoutSimple from '@/layout/OcLayoutSimple/OcLayoutSimple.vue'
-import type { OcBreadcrumbItem, OcCatalog, OcDataset, OcVocabulary } from '@/declarations'
+import type { OcBreadcrumbItem, OcDataset, OcConcept } from '@/declarations'
 import { useAccountStore } from '@/stores/account'
 import { computed } from 'vue'
 import { useI18n } from 'vue-i18n'
@@ -77,7 +77,7 @@ import OcDatasetFormStep3 from '@/components/DatasetMultiStep/OcDatasetFormStep3
 import OcDatasetFormStep4 from '@/components/DatasetMultiStep/OcDatasetFormStep4/OcDatasetFormStep4.vue'
 import OcDatasetFormStep5 from '@/components/DatasetMultiStep/OcDatasetFormStep5/OcDatasetFormStep5.vue'
 import { queryOrganizations, queryPersons } from '@/sparql/agents'
-import { getVocabulariesList, queryVocabulariesList } from '@/sparql/vocabularies'
+import { getVocabulariesList } from '@/sparql/vocabularies'
 import { onMounted } from 'vue'
 import { ref } from 'vue'
 import { insertDataset } from '@/sparql/datasets'
@@ -110,9 +110,9 @@ const dataset = ref<Partial<OcDataset>>({
   }
 })
 
-const frequencyList = ref<OcVocabulary[]>([])
-const statusList = ref<OcVocabulary[]>([])
-const conformsToList = ref<OcVocabulary[]>([])
+const frequencyList = ref<OcConcept[]>([])
+const statusList = ref<OcConcept[]>([])
+const conformsToList = ref<OcConcept[]>([])
 onMounted(async () => {
   getVocabulariesList(
     datasetsMetadata.accrualPeriodicity.vocabularies ?? [],
@@ -134,7 +134,7 @@ onMounted(async () => {
   )
 })
 
-const licenseList = ref<OcVocabulary[]>([])
+const licenseList = ref<OcConcept[]>([])
 onMounted(async () => {
   const { graph } = await getVocabulariesList(
     datasetsMetadata.conformsTo.vocabularies ?? [],
@@ -161,18 +161,6 @@ const send = () => {
   )
 }
 
-const getVocabulariesCallback = (vocabularies: string[]) => {
-  return async (query: string) => {
-    const { graph } = await queryVocabulariesList(
-      vocabularies,
-      query,
-      locale.value,
-      accountStore.auth
-    )
-    return graph
-  }
-}
-
 const activeStep = '1'
 const steps = computed(() => {
   return {
@@ -189,18 +177,16 @@ const steps = computed(() => {
           const { graph } = await queryOrganizations(query, accountStore.auth)
           return graph
         },
-        themeSearchCallback: getVocabulariesCallback(datasetsMetadata.theme.vocabularies ?? []),
-        typeSearchCallback: getVocabulariesCallback(datasetsMetadata.type.vocabularies ?? []),
+        themeVocabularies: datasetsMetadata.theme.vocabularies ?? [],
+        typeVocabularies: datasetsMetadata.type.vocabularies ?? [],
         frequencyList: frequencyList.value,
         statusList: statusList.value,
         licenseList: licenseList.value,
         conformsToList: conformsToList.value,
-        languageSearchCallback: getVocabulariesCallback(
-          datasetsMetadata.language.vocabularies ?? []
-        ),
+        languageVocabularies: datasetsMetadata.language.vocabularies ?? [],
         keywordSearchCallback: async (query: string) =>
           queryKeyword(query, locale.value, accountStore.auth),
-        spatialSearchCallback: getVocabulariesCallback(datasetsMetadata.spatial.vocabularies ?? [])
+        spatialVocabularies: datasetsMetadata.spatial.vocabularies ?? []
       },
       model: dataset.value,
       back: undefined,
diff --git a/src/sparql/vocabularies.ts b/src/sparql/vocabularies.ts
index 4b5e1b3130197720ba2e6ba6e159e4200af6e1aa..8e16045a9adf324298dc69f65ea22a318c40a158 100644
--- a/src/sparql/vocabularies.ts
+++ b/src/sparql/vocabularies.ts
@@ -1,8 +1,8 @@
-import type { Credentials, OcJsonLdDocument, OcVocabulary } from "@/declarations"
+import type { Credentials, OcJsonLdDocument, OcConcept, OcConceptScheme } from "@/declarations"
 import { executeSparqlConstruct } from "./sparql"
 import type { ContextDefinition } from "jsonld"
 
-const vocabularyContext: ContextDefinition = {
+const conceptContext: ContextDefinition = {
   "identifier": {
     "@id": "http://purl.org/dc/terms/identifier",
     "@type": "http://www.w3.org/2000/01/rdf-schema#Literal"
@@ -11,60 +11,147 @@ const vocabularyContext: ContextDefinition = {
     "@id": "http://www.w3.org/2004/02/skos/core#prefLabel",
     "@container": "@language"
   },
+  "narrower": {
+    "@id": "http://www.w3.org/2004/02/skos/core#narrower"
+  }
 }
 
-export const getVocabulariesList = async (schemes: string[], auth?: Credentials): Promise<OcJsonLdDocument<OcVocabulary>> => {
+const schemeContext: ContextDefinition = {
+  "identifier": {
+    "@id": "http://purl.org/dc/terms/identifier",
+    "@type": "http://www.w3.org/2000/01/rdf-schema#Literal"
+  },
+  "prefLabel": {
+    "@id": "http://www.w3.org/2004/02/skos/core#prefLabel",
+    "@container": "@language"
+  }
+}
+
+export const getVocabulariesList = async (schemes: string[], auth?: Credentials): Promise<OcJsonLdDocument<OcConcept>> => {
   const promiseList = schemes.map(scheme => getVocabularyList(scheme, auth))
   return Promise.all(promiseList).then((values) => {
     return {
-      context: vocabularyContext,
-      graph: ([] as OcVocabulary[]).concat(...values.map(value => value.graph))
+      context: conceptContext,
+      graph: ([] as OcConcept[]).concat(...values.map(value => value.graph))
     }
   })
 }
 
-export const getVocabularyList = async (scheme: string, auth?: Credentials): Promise<OcJsonLdDocument<OcVocabulary>> => {
-  return executeSparqlConstruct<OcVocabulary>(
+export const getVocabularyList = async (scheme: string, auth?: Credentials): Promise<OcJsonLdDocument<OcConcept>> => {
+  return executeSparqlConstruct<OcConcept>(
     `
-      WITH <${scheme}>
       CONSTRUCT {?s skos:prefLabel ?label.}
       WHERE {
         ?s a skos:Concept;
-          skos:prefLabel ?label.
+          skos:prefLabel ?label;
+          skos:inScheme <${scheme}>.
       }
     `,
     {
       auth: auth,
-      context: vocabularyContext,
+      context: conceptContext,
     }
   )
 }
 
-export const queryVocabulariesList = async (schemes: string[], query: string, locale: string, auth?: Credentials): Promise<OcJsonLdDocument<OcVocabulary>> => {
+export const queryVocabulariesList = async (schemes: string[], query: string, locale: string, auth?: Credentials): Promise<OcJsonLdDocument<OcConcept>> => {
   const promiseList = schemes.map(scheme => queryVocabulary(scheme, query, locale, auth))
   return Promise.all(promiseList).then((values) => {
     return {
-      context: vocabularyContext,
-      graph: ([] as OcVocabulary[]).concat(...values.map(value => value.graph))
+      context: conceptContext,
+      graph: ([] as OcConcept[]).concat(...values.map(value => value.graph))
     }
   })
 }
 
-export const queryVocabulary = async (scheme: string, query: string, locale: string, auth?: Credentials): Promise<OcJsonLdDocument<OcVocabulary>> => {
-  return executeSparqlConstruct<OcVocabulary>(
+export const queryVocabulary = async (scheme: string, query: string, locale: string, auth?: Credentials): Promise<OcJsonLdDocument<OcConcept>> => {
+  return executeSparqlConstruct<OcConcept>(
     `
-      WITH <${scheme}>
       CONSTRUCT {?s skos:prefLabel ?label.}
       WHERE {
         ?s a skos:Concept;
-          skos:prefLabel ?label.
+          skos:prefLabel ?label;
+          skos:inScheme <${scheme}>.
         FILTER regex(?label, "${query}", "i").
         FILTER(LANG(?label) = "${locale}").
       }
     `,
     {
       auth: auth,
-      context: vocabularyContext,
+      context: conceptContext,
+    }
+  )
+}
+
+export const getConceptChildren = async (scheme: string, parentTerm?: string, auth?: Credentials): Promise<OcJsonLdDocument<OcConcept>> => {
+    return executeSparqlConstruct<OcConcept>(
+      `
+        CONSTRUCT {
+          ?s skos:prefLabel ?label.
+          ?s skos:narrower ?narrower.
+        }
+        WHERE {
+          ?s a skos:Concept;
+            skos:prefLabel ?label;
+            skos:broader|^skos:narrower <${parentTerm}>;
+            skos:inScheme <${scheme}>.
+          OPTIONAL {
+            ?s skos:narrower|^skos:broader ?narrower
+          }
+        }
+      `,
+      {
+        auth: auth,
+        context: conceptContext,
+      }
+    )
+}
+
+export const getVocabularyRootConcepts = async (scheme: string, auth?: Credentials): Promise<OcJsonLdDocument<OcConcept>> => {
+  const result = await executeSparqlConstruct<OcConcept>(
+    `
+      CONSTRUCT {
+        ?s skos:prefLabel ?label.
+        ?s skos:narrower ?narrower.
+      }
+      WHERE {
+        ?s a skos:Concept;
+          skos:prefLabel ?label;
+          skos:topConceptOf|^skos:hasTopConcept <${scheme}>.
+        OPTIONAL {
+          ?s skos:narrower|^skos:broader ?narrower
+        }
+      }
+    `,
+    {
+      auth: auth,
+      context: conceptContext,
+    }
+  )
+
+  if (result.graph.length){
+    return result
+  } else {
+    // Si il n'y a pas de résulats il peut s'agir d'un vocabulaire sans arborescence et donc on utilise `getVocabularyList`
+    return getVocabularyList(scheme, auth)
+  }
+}
+
+export const getVocabulariesInformations = async (schemes: string[]) => {
+  const schemesValues = '<' + schemes.join('> <') + '>'
+  return executeSparqlConstruct<OcConceptScheme>(
+    `
+      CONSTRUCT {
+        ?s ?p ?o.
+      }
+      WHERE {
+        VALUES ?s { ${schemesValues} }
+        VALUES ?p { skos:prefLabel }
+        ?s ?p ?o.
+      }
+    `,
+    {
+      context: schemeContext,
     }
   )
 }
\ No newline at end of file