diff --git a/src/components/Exploration/ExplorationError.spec.tsx b/src/components/Exploration/ExplorationError.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..84528c4e7f10ac68a5fa21b0922a74fc38de4e72
--- /dev/null
+++ b/src/components/Exploration/ExplorationError.spec.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import DuelError from 'components/Duel/DuelError'
+
+jest.mock('cozy-ui/transpiled/react/I18n', () => {
+  return {
+    useI18n: jest.fn(() => {
+      return {
+        t: (str: string) => str,
+      }
+    }),
+  }
+})
+
+describe('DuelError component', () => {
+  it('should be rendered correctly', () => {
+    const component = shallow(<DuelError />).getElement
+    expect(component).toMatchSnapshot()
+  })
+})
diff --git a/src/components/Exploration/ExplorationError.tsx b/src/components/Exploration/ExplorationError.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9f1728e483f45a1e39a07e85e38e5d5246eb19c0
--- /dev/null
+++ b/src/components/Exploration/ExplorationError.tsx
@@ -0,0 +1,29 @@
+import React, { useCallback } from 'react'
+import { useI18n } from 'cozy-ui/transpiled/react/I18n'
+import { useHistory } from 'react-router-dom'
+import './explorationError.scss'
+import StyledStopButton from 'components/CommonKit/Button/StyledStopButton'
+
+const ExplorationError: React.FC = () => {
+  const { t } = useI18n()
+  const history = useHistory()
+
+  const goBack = useCallback(() => {
+    history.goBack()
+  }, [history])
+
+  return (
+    <div className="exploration-error-container">
+      <div className="exploration-error-message">
+        {t('exploration.global_error')}
+      </div>
+      <div className="exploration-error-button">
+        <StyledStopButton color="secondary" onClick={goBack}>
+          {t('exploration.error_go_back')}
+        </StyledStopButton>
+      </div>
+    </div>
+  )
+}
+
+export default ExplorationError
diff --git a/src/components/Exploration/ExplorationOngoing.spec.tsx b/src/components/Exploration/ExplorationOngoing.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..35823c9b0e372ca95646832055253b0bf527bd35
--- /dev/null
+++ b/src/components/Exploration/ExplorationOngoing.spec.tsx
@@ -0,0 +1,126 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { Provider } from 'react-redux'
+import { act } from 'react-dom/test-utils'
+import configureStore from 'redux-mock-store'
+import DuelOngoing from 'components/Duel/DuelOngoing'
+import { UserChallenge } from 'models'
+import { DateTime } from 'luxon'
+import { UserChallengeState } from 'enum/userChallenge.enum'
+import { globalStateData } from '../../../test/__mocks__/globalStateData.mock'
+import { userChallengeData } from '../../../test/__mocks__/userChallengeData.mock'
+import { challengeStateData } from '../../../test/__mocks__/challengeStateData.mock'
+
+jest.mock('cozy-ui/transpiled/react/I18n', () => {
+  return {
+    useI18n: jest.fn(() => {
+      return {
+        t: (str: string) => str,
+      }
+    }),
+  }
+})
+
+const mockUserChallengeUpdateFlag = jest.fn()
+const mockIsChallengeDone = jest.fn()
+jest.mock('services/challenge.service', () => {
+  return jest.fn(() => {
+    return {
+      updateUserChallenge: mockUserChallengeUpdateFlag,
+      isChallengeDone: mockIsChallengeDone,
+    }
+  })
+})
+
+const mockHistoryPush = jest.fn()
+jest.mock('react-router-dom', () => ({
+  ...jest.requireActual('react-router-dom'),
+  useHistory: () => ({
+    push: mockHistoryPush,
+  }),
+}))
+
+jest.mock('components/Duel/DuelChart', () => 'mock-duelchart')
+
+const mockStore = configureStore([])
+
+describe('DuelOngoing component', () => {
+  it('should be rendered correctly', () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+        challenge: challengeStateData,
+      },
+    })
+    mockIsChallengeDone.mockResolvedValueOnce({ isDone: false, isWin: false })
+    const wrapper = mount(
+      <Provider store={store}>
+        <DuelOngoing userChallenge={userChallengeData[0]} />
+      </Provider>
+    )
+    expect(wrapper.find('.duel-title').text()).toEqual(
+      userChallengeData[1].duel.title
+    )
+    expect(wrapper.find('.duel-goal').exists()).toBeTruthy()
+    expect(wrapper.find('.duel-consumption').exists()).toBeTruthy()
+    expect(wrapper.find('.duel-chart').exists()).toBeTruthy()
+    expect(wrapper.find('.caption-icon').exists()).toBeTruthy()
+  })
+
+  it('should display the result modal', async () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+        challenge: challengeStateData,
+      },
+    })
+    const updatedUserChallenge: UserChallenge = {
+      ...userChallengeData[0],
+      state: UserChallengeState.DUEL,
+      startDate: DateTime.local().setZone('utc', { keepLocalTime: true }),
+    }
+    mockIsChallengeDone.mockResolvedValue({ isDone: true, isWin: true })
+    const wrapper = mount(
+      <Provider store={store}>
+        <DuelOngoing userChallenge={updatedUserChallenge} />
+      </Provider>
+    )
+    await act(async () => {
+      await new Promise(resolve => setTimeout(resolve))
+      wrapper.update()
+    })
+    expect(wrapper.find('DuelResultModal').props().open).toBeTruthy()
+  })
+
+  it('should be redirected to challenges when modal button is clicked', async () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+        challenge: challengeStateData,
+      },
+    })
+    const updatedUserChallenge: UserChallenge = {
+      ...userChallengeData[0],
+      state: UserChallengeState.DUEL,
+      startDate: DateTime.local().setZone('utc', { keepLocalTime: true }),
+    }
+    mockIsChallengeDone.mockResolvedValue({ isDone: true, isWin: true })
+    mockUserChallengeUpdateFlag.mockResolvedValue(updatedUserChallenge)
+    const wrapper = mount(
+      <Provider store={store}>
+        <DuelOngoing userChallenge={updatedUserChallenge} />
+      </Provider>
+    )
+    await act(async () => {
+      await new Promise(resolve => setTimeout(resolve))
+      wrapper.update()
+    })
+    wrapper.find('StyledStopButton').simulate('click')
+
+    await act(async () => {
+      await new Promise(resolve => setTimeout(resolve))
+      wrapper.update()
+    })
+    expect(mockHistoryPush).toHaveBeenCalledWith('/challenges')
+  })
+})
diff --git a/src/components/Exploration/ExplorationOngoing.tsx b/src/components/Exploration/ExplorationOngoing.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..57e3270abe633c76fbc8fc0f8241fcecd4509b0d
--- /dev/null
+++ b/src/components/Exploration/ExplorationOngoing.tsx
@@ -0,0 +1,38 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import { useHistory } from 'react-router-dom'
+import './explorationOngoing.scss'
+import { Client, useClient } from 'cozy-client'
+import { useI18n } from 'cozy-ui/transpiled/react/I18n'
+import { useDispatch, useSelector } from 'react-redux'
+import { AppStore } from 'store'
+
+import { UserExploration, UserChallenge } from 'models'
+import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum'
+
+interface ExplorationOngoingProps {
+  userChallenge: UserChallenge
+}
+
+const ExplorationOngoing: React.FC<ExplorationOngoingProps> = ({
+  userChallenge,
+}: ExplorationOngoingProps) => {
+  const client: Client = useClient()
+  const { t } = useI18n()
+  const { currentDataload } = useSelector(
+    (state: AppStore) => state.ecolyo.challenge
+  )
+  const dispatch = useDispatch()
+  const history = useHistory()
+
+  // useEffect(() => {
+
+  // }, [])
+
+  return (
+    <>
+      <div className="exploration-ongoing-container"></div>
+    </>
+  )
+}
+
+export default ExplorationOngoing
diff --git a/src/components/Exploration/ExplorationView.spec.tsx b/src/components/Exploration/ExplorationView.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8bfbfca09281fd632ada8ea3940da5053e8dc114
--- /dev/null
+++ b/src/components/Exploration/ExplorationView.spec.tsx
@@ -0,0 +1,95 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+import * as reactRedux from 'react-redux'
+import DuelView from 'components/Duel/DuelView'
+import { UserChallengeState } from 'enum/userChallenge.enum'
+import { challengeStateData } from '../../../test/__mocks__/challengeStateData.mock'
+import { userChallengeData } from '../../../test/__mocks__/userChallengeData.mock'
+import DuelError from './DuelError'
+import DuelUnlocked from './DuelUnlocked'
+import DuelOngoing from './DuelOngoing'
+import { UserDuelState } from 'enum/userDuel.enum'
+
+const mockUseSelector = jest.spyOn(reactRedux, 'useSelector')
+
+describe('DuelView component', () => {
+  it('should be rendered with DuelError component when no current challenge', () => {
+    mockUseSelector.mockReturnValue(challengeStateData)
+    const wrapper = shallow(<DuelView />)
+    expect(wrapper.find(DuelError).exists()).toBeTruthy()
+  })
+
+  it('should be rendered with DuelError component when current challenge with state = duel and duel state = done', () => {
+    const updatedUserChallenge = {
+      ...userChallengeData[1],
+      state: UserChallengeState.DUEL,
+      duel: { ...userChallengeData[1].duel, state: UserDuelState.DONE },
+    }
+    const updatedChallengeState = {
+      ...challengeStateData,
+      currentChallenge: updatedUserChallenge,
+    }
+    mockUseSelector.mockReturnValue(updatedChallengeState)
+    const wrapper = shallow(<DuelView />)
+    expect(wrapper.find(DuelError).exists()).toBeTruthy()
+  })
+
+  it('should be rendered with DuelError component when current challenge with state = duel and duel state = locked', () => {
+    const updatedUserChallenge = {
+      ...userChallengeData[1],
+      state: UserChallengeState.DUEL,
+      duel: { ...userChallengeData[1].duel, state: UserDuelState.LOCKED },
+    }
+    const updatedChallengeState = {
+      ...challengeStateData,
+      currentChallenge: updatedUserChallenge,
+    }
+    mockUseSelector.mockReturnValue(updatedChallengeState)
+    const wrapper = shallow(<DuelView />)
+    expect(wrapper.find(DuelError).exists()).toBeTruthy()
+  })
+
+  it('should be rendered with DuelError component when current challenge with state != duel ', () => {
+    const updatedUserChallenge = {
+      ...userChallengeData[1],
+      state: UserChallengeState.ONGOING,
+    }
+    const updatedChallengeState = {
+      ...challengeStateData,
+      currentChallenge: updatedUserChallenge,
+    }
+    mockUseSelector.mockReturnValue(updatedChallengeState)
+    const wrapper = shallow(<DuelView />)
+    expect(wrapper.find(DuelError).exists()).toBeTruthy()
+  })
+
+  it('should be rendered with DuelUnlocked component when current challenge with state = duel  and duel state = unlocked', () => {
+    const updatedUserChallenge = {
+      ...userChallengeData[1],
+      state: UserChallengeState.DUEL,
+      duel: { ...userChallengeData[1].duel, state: UserDuelState.UNLOCKED },
+    }
+    const updatedChallengeState = {
+      ...challengeStateData,
+      currentChallenge: updatedUserChallenge,
+    }
+    mockUseSelector.mockReturnValue(updatedChallengeState)
+    const wrapper = shallow(<DuelView />)
+    expect(wrapper.find(DuelUnlocked).exists()).toBeTruthy()
+  })
+
+  it('should be rendered with DuelOngoing component when current challenge with state = duel  and duel state = ongoing', () => {
+    const updatedUserChallenge = {
+      ...userChallengeData[1],
+      state: UserChallengeState.DUEL,
+      duel: { ...userChallengeData[1].duel, state: UserDuelState.ONGOING },
+    }
+    const updatedChallengeState = {
+      ...challengeStateData,
+      currentChallenge: updatedUserChallenge,
+    }
+    mockUseSelector.mockReturnValue(updatedChallengeState)
+    const wrapper = shallow(<DuelView />)
+    expect(wrapper.find(DuelOngoing).exists()).toBeTruthy()
+  })
+})
diff --git a/src/components/Exploration/ExplorationView.tsx b/src/components/Exploration/ExplorationView.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..80be928f8e49be36a3818f6cd8b8a5ff83a48044
--- /dev/null
+++ b/src/components/Exploration/ExplorationView.tsx
@@ -0,0 +1,57 @@
+import React, { useCallback, useState } from 'react'
+import { useSelector } from 'react-redux'
+import { AppStore } from 'store'
+import CozyBar from 'components/Header/CozyBar'
+import Content from 'components/Content/Content'
+import Header from 'components/Header/Header'
+import { UserChallengeState } from 'enum/userChallenge.enum'
+
+import { UserChallenge } from 'models'
+
+import { useHistory } from 'react-router-dom'
+import { UserExplorationState } from 'enum/userExploration.enum'
+import ExplorationError from './ExplorationError'
+import ExplorationOngoing from './ExplorationOngoing'
+
+const ExplorationView: React.FC = () => {
+  const [headerHeight, setHeaderHeight] = useState<number>(0)
+  const { currentChallenge } = useSelector(
+    (state: AppStore) => state.ecolyo.challenge
+  )
+  const history = useHistory()
+  const defineHeaderHeight = useCallback((height: number) => {
+    setHeaderHeight(height)
+  }, [])
+
+  const renderExploration = (challenge: UserChallenge) => {
+    switch (challenge.exploration.state) {
+      case UserExplorationState.ONGOING:
+        return <ExplorationOngoing userChallenge={challenge} />
+      default:
+        return <ExplorationError />
+    }
+  }
+
+  return (
+    <>
+      <CozyBar titleKey={'COMMON.APP_DUEL_TITLE'} displayBackArrow={true} />
+      <Header
+        setHeaderHeight={defineHeaderHeight}
+        desktopTitleKey={'COMMON.APP_DUEL_TITLE'}
+        displayBackArrow={true}
+      ></Header>
+      <Content height={headerHeight}>
+        <div>
+          {currentChallenge &&
+          currentChallenge.state === UserChallengeState.DUEL ? (
+            renderExploration(currentChallenge)
+          ) : (
+            <ExplorationError />
+          )}
+        </div>
+      </Content>
+    </>
+  )
+}
+
+export default ExplorationView
diff --git a/src/components/Exploration/explorationError.scss b/src/components/Exploration/explorationError.scss
new file mode 100644
index 0000000000000000000000000000000000000000..de5b9d02f9676afbd041a997cdf0cf52f2e0968d
--- /dev/null
+++ b/src/components/Exploration/explorationError.scss
@@ -0,0 +1,19 @@
+@import '../../styles/base/typography';
+
+.exploration-error-container{
+  display: flex;
+  min-height: 60vh;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 1rem 1.5rem;
+  color: $grey-bright;
+}
+.exploration-error-message{
+  margin-top: 3rem;
+  text-align: center;
+}
+.exploration-error-button{
+  margin-top: 3rem;
+  width: 7.5rem;
+}
\ No newline at end of file
diff --git a/src/components/Exploration/explorationOngoing.scss b/src/components/Exploration/explorationOngoing.scss
new file mode 100644
index 0000000000000000000000000000000000000000..0f414f8cfce76feae0e01664eed3432e93667e10
--- /dev/null
+++ b/src/components/Exploration/explorationOngoing.scss
@@ -0,0 +1,47 @@
+@import '../../styles/base/typography';
+
+.duel-ongoing-container{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+.duel-title{
+  color: $soft-grey;
+  margin-top: 1rem;
+}
+.duel-goal{
+  color: $grey-bright;
+  margin: 1rem 3rem;
+  text-align: center;
+}
+.duel-consumption{
+  color: $grey-bright;
+  margin: 1rem auto;
+  .consumption{
+    color: $gold-light;
+
+  }
+}
+.duel-chart{
+  height: 15.625rem;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 80%;
+}
+.duel-chart-caption{
+  display: flex;
+  flex-direction: column;
+  align-self: flex-start;
+  .duel-caption{
+    display: flex;
+    margin-top: 0.75rem;
+    .caption-icon{
+      margin: auto 1.5rem;
+    }
+    .caption-label{
+      color: $grey-bright;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/components/Exploration/explorationView.scss b/src/components/Exploration/explorationView.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6708d11d3c270338fc2f69a8bb15f815da5b7134
--- /dev/null
+++ b/src/components/Exploration/explorationView.scss
@@ -0,0 +1 @@
+@import '../../styles/base/typography';
\ No newline at end of file
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 1482329dbc2d7db4f8e3677314016ece6570d809..2c442e8fbbb05ac131c9dd32e6f355501d335a14 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -373,5 +373,9 @@
     "confirm": "Valider",
     "next": "Suivant",
     "consumption_question": "Question sur votre consommation"
+  },
+  "exploration": {
+    "global_error": "Oups.. Une erreur est parvenue. Veuillez retourner à l'écran des défis",
+    "go_back": "Retour"
   }
 }