diff --git a/src/Gargantext/Components/DocsTable.purs b/src/Gargantext/Components/DocsTable.purs
index 66ba2bacadaeb1a5043c953cfa850511bbebbe52..f98229d494021317ac94a3b4749ae89339e148b0 100644
--- a/src/Gargantext/Components/DocsTable.purs
+++ b/src/Gargantext/Components/DocsTable.purs
@@ -3,11 +3,12 @@ module Gargantext.Components.DocsTable where
 
 import Gargantext.Prelude
 
+import CSS (query)
 import DOM.Simple.Event as DE
-import Data.Array (any)
 import Data.Array as A
+import Data.Either (Either(..))
 import Data.Generic.Rep (class Generic)
-import Data.Lens ((^.))
+import Data.Lens (is, re, (^.))
 import Data.Lens.At (at)
 import Data.Lens.Record (prop)
 import Data.Map as Map
@@ -20,32 +21,30 @@ import Data.String as Str
 import Data.Tuple (Tuple(..))
 import Data.Tuple.Nested ((/\))
 import Effect (Effect)
-import Effect.Aff (Aff, launchAff_)
+import Effect.Aff (launchAff_)
 import Effect.Class (liftEffect)
-import Effect.Timer (setTimeout)
-import Gargantext.AsyncTasks as GAT
 import Gargantext.Components.App.Store as Store
 import Gargantext.Components.Bootstrap as B
-import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), ModalSizing(..), Variant(..))
+import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), ModalSizing(..), Variant(..), SpinnerTheme(..))
 import Gargantext.Components.Category (ratingSimple)
 import Gargantext.Components.Category.Types (Category(..), cat2score, markCategoryChecked)
 import Gargantext.Components.DocsTable.DocumentFormCreation as DFC
-import Gargantext.Components.DocsTable.Types (DocumentsView(..), Hyperdata(..), LocalCategories, Query, Response(..), Year, sampleData, showSource)
+import Gargantext.Components.DocsTable.SubcorpusCreation (subcorpusCreation)
+import Gargantext.Components.DocsTable.Types (DocumentsView(..), Hyperdata(..), LocalCategories, Query, Response(..), SubcorpusParams(..), Year, createSubCorpus, sampleData, showSource)
 import Gargantext.Components.GraphQL.Endpoints (updateNodeContextCategory)
+import Gargantext.Components.Modal (modal)
 import Gargantext.Components.Nodes.Lists.Types as NT
 import Gargantext.Components.Reload (textsReloadContext)
 import Gargantext.Components.Table as TT
 import Gargantext.Components.Table.Types as TT
 import Gargantext.Config.REST (AffRESTError)
-import Gargantext.Config.Utils (handleRESTError)
 import Gargantext.Ends (Frontends, url)
 import Gargantext.Hooks.LinkHandler (useLinkHandler)
 import Gargantext.Hooks.Loader (useLoader, useLoaderWithCacheAPI, HashedResponse(..))
-import Gargantext.Routes (SessionRoute(NodeAPI))
+import Gargantext.Routes (SessionRoute(..))
 import Gargantext.Routes as Routes
-import Gargantext.Sessions (Session, sessionId, get, delete)
+import Gargantext.Sessions (Session, delete, get, sessionId)
 import Gargantext.Types (ListId, NodeID, NodeType(..), OrderBy(..), SidePanelState(..), TabSubType, TabType, TableResult, showTabType')
-import Gargantext.Types as GT
 import Gargantext.Utils (sortWith, (?))
 import Gargantext.Utils.CacheAPI as GUC
 import Gargantext.Utils.QueryString (joinQueryStrings, mQueryParamS, mQueryParamS', queryParam, queryParamS)
@@ -147,12 +146,15 @@ docViewCpt = R2.hereComponent here "docView" hCpt
     }
     _ = do
     -- State
-    { errors } <- Store.use
+    { errors, reloadForest } <- Store.use
     cacheState' <- T.useLive T.unequal cacheState
     query' <- T.useLive T.unequal query
     isDocumentModalVisibleBox <- T.useBox false
+    isSubcorpusModalVisibleBox <- T.useBox false
     onDocumentCreationPending /\ onDocumentCreationPendingBox <-
       R2.useBox' false
+    onSubcorpusCreationPending' /\ onSubcorpusCreationPending <- R2.useBox' false
+    { goToRoute } <- useLinkHandler
 
     -- Context
     mReloadContext <- R.useContext textsReloadContext
@@ -181,6 +183,22 @@ docViewCpt = R2.hereComponent here "docView" hCpt
 
       liftEffect $ here.log "[docView] TODO onCreateDocumentEnd handler"
 
+    createSubcorpusCallback <- pure $ \q p -> launchAff_ do
+
+      liftEffect $
+        T.write_ true onSubcorpusCreationPending
+
+      case mCorpusId of
+        Nothing -> liftEffect $ here.warn2 "[docsTable subCorpusButton RESTError]" mCorpusId
+        Just cId -> do
+          res <- createSubCorpus session cId $ SubcorpusParams { query: q, reuseParentList: p }
+          liftEffect $ do
+            case res of
+              Left err -> here.warn2 "[docsTable subCorpusButton RESTError]" err
+              Right id -> do
+                T2.reload reloadForest
+                goToRoute $ Routes.Corpus (sessionId session) id
+
     -- handleRESTError hp errors eTask
     --   \t -> liftEffect $ launchDocumentCreationProgress
     --                         errors
@@ -208,7 +226,7 @@ docViewCpt = R2.hereComponent here "docView" hCpt
                         ]
                     ]
                 , H.div { className: "form-group" }
-                    [ if showSearch then searchBar { query } [] else H.div {} [] ]
+                    [ if showSearch then searchBar { query, isSubcorpusModalVisibleBox, onSubcorpusCreationPending' } [] else H.div {} [] ]
                 ]
 
             , R2.row
@@ -245,6 +263,20 @@ docViewCpt = R2.hereComponent here "docView" hCpt
                 , status: onDocumentCreationPending ? Deferred $ Enabled
                 }
             ]
+        ,
+          -- Subcorpus Creation Modal
+          B.baseModal
+            { isVisibleBox: isSubcorpusModalVisibleBox
+            , title: Just "Create a subcorpus"
+            , hasCollapsibleBackground: false
+            , size: MediumModalSize
+            }
+            [ subcorpusCreation
+                { callback: createSubcorpusCallback
+                , query'
+                , onSubcorpusCreationPending'
+                }
+            ]
         ]
 
 -- launchDocumentCreationProgress ::
@@ -289,7 +321,7 @@ docViewCpt = R2.hereComponent here "docView" hCpt
 ---------------------------------------------------
 
 type SearchBarProps =
-  (query :: T.Box Query)
+  (query :: T.Box Query, isSubcorpusModalVisibleBox :: T.Box Boolean, onSubcorpusCreationPending' :: Boolean)
 
 searchBar :: R2.Component SearchBarProps
 searchBar = R.createElement searchBarCpt
@@ -297,33 +329,45 @@ searchBar = R.createElement searchBarCpt
 searchBarCpt :: R.Component SearchBarProps
 searchBarCpt = here.component "searchBar" cpt
   where
-  cpt { query } _children = do
+  cpt { query, isSubcorpusModalVisibleBox, onSubcorpusCreationPending' } _children = do
     query' <- T.useLive T.unequal query
     queryText <- T.useBox query'
     queryText' <- T.useLive T.unequal queryText
 
-    pure $ H.div { className: "input-group px-5" }
-      [ H.input
-          { className: "form-control"
-          , id: "docs-input-search"
-          , defaultValue: query'
-          , on:
-              { change: onSearchChange queryText
-              , keyUp: onSearchKeyup query queryText'
+    pure $ R.fragment
+      [ H.div { className: "input-group px-5" }
+          [ H.input
+              { className: "form-control"
+              , id: "docs-input-search"
+              , defaultValue: query'
+              , on:
+                  { change: onSearchChange queryText
+                  , keyUp: onSearchKeyup query queryText'
+                  }
+              , placeholder: "Search in documents"
+              , type: "text"
               }
-          , placeholder: "Search in documents"
-          , type: "text"
-          }
-      , H.div { className: "input-group-append" }
-          [ if query' /= "" then
-              R.fragment
-                [ clearButton query
-                , searchButton query queryText'
-                ]
-            else
-              searchButton query queryText'
+          , H.div { className: "input-group-append" }
+              [ if query' /= "" then
+                  R.fragment
+                    [ clearButton query
+                    , searchButton query queryText'
+                    , subCorpusButton isSubcorpusModalVisibleBox queryText' query
+                    ]
+                else
+                  R.fragment
+                    [ searchButton query queryText'
+                    , subCorpusButton isSubcorpusModalVisibleBox queryText' query
+                    ]
+              ]
+          , H.div { className: "input-group-append" }
+              [ R2.when' onSubcorpusCreationPending'
+                  [ B.spinner
+                      { theme: BorderTheme }
+                  ]
+              ]
+          -- , H.div {className: "col-md-1"} [ searchButton query queryText' ]
           ]
-      -- , H.div {className: "col-md-1"} [ searchButton query queryText' ]
       ]
 
   onSearchChange :: forall e. T.Box Query -> e -> Effect Unit
@@ -352,6 +396,19 @@ searchBarCpt = here.component "searchBar" cpt
       }
       [ H.span { className: "text-danger fa fa-times" } [] ]
 
+  subCorpusButton modalVisible queryText' query =
+    H.button
+      { className: "input-group-text btn btn-light text-secondary"
+      , on:
+          { click: \_ -> do
+              T.write_ queryText' query
+              T.write_ true modalVisible
+          }
+      , type: "submit"
+      , title: "Create a subcorpus"
+      }
+      [ H.span { className: "fa fa-filter" } [] ]
+
 mock :: Boolean
 mock = false
 
diff --git a/src/Gargantext/Components/DocsTable/SubcorpusCreation.purs b/src/Gargantext/Components/DocsTable/SubcorpusCreation.purs
new file mode 100644
index 0000000000000000000000000000000000000000..d1349fc1d300e3d2459aaf0a51a9339d5b7f0af1
--- /dev/null
+++ b/src/Gargantext/Components/DocsTable/SubcorpusCreation.purs
@@ -0,0 +1,60 @@
+module Gargantext.Components.DocsTable.SubcorpusCreation where
+
+import Gargantext.Prelude
+
+import Effect (Effect)
+import Gargantext.Hooks.StateRecord (useStateRecord)
+import Gargantext.Components.Bootstrap as B
+import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..))
+import Gargantext.Utils.Reactix as R2
+import Reactix as R
+import Reactix.DOM.HTML as H
+import Toestand as T
+
+type Props =
+  ( callback :: String -> Boolean -> Effect Unit
+  , query' :: String
+  , onSubcorpusCreationPending' :: Boolean
+  )
+
+subcorpusCreation :: R2.Leaf Props
+subcorpusCreation = R2.leaf component
+
+component :: R.Component Props
+component = R.hooksComponent "subcorpusCreation" cpt
+  where
+  cpt { query', onSubcorpusCreationPending', callback } _ = do
+    { state, stateBox } <- useStateRecord (defaultData :: FormData)
+
+    let
+      onParentListCheckboxChange :: Boolean -> Effect Unit
+      onParentListCheckboxChange value = T.modify_
+        (\prev -> prev { reuseParentList = value })
+        stateBox
+
+    pure $ H.div {}
+      [ H.div { className: "form-group" }
+          [ H.label {} [ H.text $ "Creating subcorpus from query: " <> query' ]
+          ]
+      , H.div { className: "form-check" }
+          [ B.formCheckbox
+              { value: state.reuseParentList
+              , callback: onParentListCheckboxChange
+              }
+          , H.label { className: "form-check-label" } [ H.text "Reuse parent list?" ]
+          ]
+      , B.button
+          { callback: \_ -> callback query' state.reuseParentList
+          , type: "submit"
+          , variant: ButtonVariant Primary
+          , status: if query' == "" then Disabled else if onSubcorpusCreationPending' then Deferred else Enabled
+          }
+          [ H.text "Create!" ]
+      ]
+
+type FormData = { reuseParentList :: Boolean }
+
+defaultData :: FormData
+defaultData =
+  { reuseParentList: true
+  }
diff --git a/src/Gargantext/Components/DocsTable/Types.purs b/src/Gargantext/Components/DocsTable/Types.purs
index 148d5fb162177cf7f382ed90cc786bc72484f3bb..52d3ec965a05c15c9aa3be350c8cacfe2b1c9714 100644
--- a/src/Gargantext/Components/DocsTable/Types.purs
+++ b/src/Gargantext/Components/DocsTable/Types.purs
@@ -1,12 +1,17 @@
 module Gargantext.Components.DocsTable.Types where
 
+import Gargantext.Prelude
+
 import Data.Eq.Generic (genericEq)
 import Data.Generic.Rep (class Generic)
 import Data.Map (Map)
 import Data.Maybe (Maybe(..), fromMaybe)
 import Data.Tuple (Tuple(..))
 import Gargantext.Components.Category.Types (Category(..), Star, decodeCategory)
-import Gargantext.Prelude
+import Gargantext.Config.REST (AffRESTError)
+import Gargantext.Routes (SessionRoute(..))
+import Gargantext.Sessions (Session, post)
+import Gargantext.Types (NodeID)
 import Simple.JSON as JSON
 
 data Action = MarkCategory Int Category
@@ -115,6 +120,19 @@ type LocalUserScore = Map Int Star
 type Query = String
 type Year = String
 
+newtype SubcorpusParams = SubcorpusParams
+  { query :: Query
+  , reuseParentList :: Boolean
+  }
+
+derive instance Eq SubcorpusParams
+derive instance Generic SubcorpusParams _
+derive newtype instance JSON.ReadForeign SubcorpusParams
+derive newtype instance JSON.WriteForeign SubcorpusParams
+
+createSubCorpus :: Session -> Int -> SubcorpusParams -> AffRESTError NodeID
+createSubCorpus session parentId = post session (SubCorpus parentId)
+
 ---------------------------------------------------------
 sampleData' :: DocumentsView
 sampleData' = DocumentsView
diff --git a/src/Gargantext/Components/Login/LoginForm.purs b/src/Gargantext/Components/Login/LoginForm.purs
index 8d77a70281cadc33ff2c53dcee27c0d4a9770a37..06c5c04193120e054f8151e8f006fa029cc050ba 100644
--- a/src/Gargantext/Components/Login/LoginForm.purs
+++ b/src/Gargantext/Components/Login/LoginForm.purs
@@ -30,7 +30,6 @@ import Gargantext.Utils.Reactix as R2
 import Reactix as R
 import Reactix.DOM.HTML as H
 import Record as Record
-import Record.Unsafe (unsafeSet)
 import Toestand as T
 
 here :: R2.Here
@@ -119,13 +118,13 @@ componentCpt = here.component "main" cpt
       -- @XXX StateRecord with distinct value types
       onAgreedCheckboxChange :: Boolean -> Effect Unit
       onAgreedCheckboxChange value = T.modify_
-        (\prev -> unsafeSet "agreed" value prev)
+        (\prev -> prev { agreed = value })
         stateBox
 
       -- @XXX StateRecord with distinct value types
       onAgreedLabelClick :: Unit -> Effect Unit
       onAgreedLabelClick _ = T.modify_
-        (\prev -> unsafeSet "agreed" (not state.agreed) prev)
+        (\prev -> prev { agreed = not state.agreed })
         stateBox
 
     -- | Render
diff --git a/src/Gargantext/Ends.purs b/src/Gargantext/Ends.purs
index 0bc4d0a29f98edd442ecfe4e7cb69294170bba90..8dafe4a64852a969e5fef76c942966eb24d07cb4 100644
--- a/src/Gargantext/Ends.purs
+++ b/src/Gargantext/Ends.purs
@@ -273,6 +273,7 @@ sessionPath (R.ChartHash { chartType, listId, tabType } i) =
 sessionPath (R.PhyloAPI nId) = "node/" <> show nId <> "/phylo"
 sessionPath R.Members = "members"
 sessionPath (R.ShareURL i t) = "shareurl?type=" <> show t <> "&id=" <> show i
+sessionPath (R.SubCorpus i) = "corpus/" <> show i <> "/subcorpus"
 
 ------- misc routing stuff
 
diff --git a/src/Gargantext/Routes.purs b/src/Gargantext/Routes.purs
index 1076c5cfe128ce87dfac719f1e615bf10826f8fd..7a4af1e1020895c8b0a49176162ba342d78045dd 100644
--- a/src/Gargantext/Routes.purs
+++ b/src/Gargantext/Routes.purs
@@ -148,6 +148,7 @@ data SessionRoute
   | PhyloAPI Id
   | Members
   | ShareURL Id NodeType
+  | SubCorpus Id
 
 ------------------------------------------------------