diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95edf2bb7..17148a068 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,373 +1,373 @@
-# FoTT Changelog 
-## What's new in Form Recognizer?
-Click [here](https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/whats-new) to see what's new in Form Recognizer.
-
-## Released conatiner's currently referenced commit
-2.1-Preview's released container image, tracked by the `latest-preview` image tag in our [docker hub repository](https://hub.docker.com/_/microsoft-azure-cognitive-services-custom-form-labeltool), currently references **2.1-preview.2-b6b9a2f (12-10-2020)**
-
-## Commit history
-### 2.1-preview.2-b6b9a2f (12-10-2020)
-* update appVersion to 2.1.2 ([#808](https://github.com/microsoft/OCR-Form-Tools/commit/b6b9a2f131485d08541a1e85f6af59ebfbeca773))
-* add locale in prebuiltPredictPage ([#772](https://github.com/microsoft/OCR-Form-Tools/commit/06d9c16a7c1fe64a95d835878dcb8dabb8c7e485)) ([#776](https://github.com/microsoft/OCR-Form-Tools/commit/06d9c16a7c1fe64a95d835878dcb8dabb8c7e485))
-* Stew ro/cherry pick 347e21e 2b6ead7 ([#766](https://github.com/microsoft/OCR-Form-Tools/commit/ca59cee26587e1aee49507abd75c0683d67f541f))
-* Cherry pick 34ce14d a7ccb34 ([#763](https://github.com/microsoft/OCR-Form-Tools/commit/244c23df700791794990eb6f7e196bcf9ff9c844))
-* Stew ro/cherry pick ab5a8a8 abfffbb ([#760](https://github.com/microsoft/OCR-Form-Tools/commit/93b7a2d4d7688cda4baa4cfd704b2666df524174))
-* refactor: disable api version selection ([#755](https://github.com/microsoft/OCR-Form-Tools/commit/be1f18db0b7073dad106f443f343aa221dea7fc6))
-* refactor: disable draw region button ([#756](https://github.com/microsoft/OCR-Form-Tools/commit/8816a85761a795f3f0b34b87360236cadd80a735))
-* feat: support null text values in analyze results ([#744](https://github.com/microsoft/OCR-Form-Tools/commit/0ddb7f1275d6f195c2af9f0b7053987e01a5d677))
-* feat: support rowspan and column span for layout tables ([#754](https://github.com/microsoft/OCR-Form-Tools/commit/6994ac929146e25370d1461e4e502773de3d5503))
-* Update changelog ([#750](https://github.com/microsoft/OCR-Form-Tools/commit/acba3966ce960e474dfd3d97510b07c108e7b39f))
-* check whether the label data is null ([#753](https://github.com/microsoft/OCR-Form-Tools/commit/f6ef41ac1500e52f3714eda6e99187e016e1b223))
-* fix issue of 773 ([#740](https://github.com/microsoft/OCR-Form-Tools/commit/9d35e79393cdb0de678e9ec6b850daf5a4df5c96))
-
-### 2.1-preview.1-2e50498 (11-09-2020)
-* fix: enable api version selection ([#736](https://github.com/microsoft/OCR-Form-Tools/commit/2e5049883bd1550ba80210edca7db4233d7a15fa))
-* fix: labeling doesn't work via shortcuts on the new project or empty tags ([#677](https://github.com/microsoft/OCR-Form-Tools/commit/f11291940b776ceb8ba7708e6f58dc2572f7b01b))
-* fix: remove setting project state in project form on change ([#732](https://github.com/microsoft/OCR-Form-Tools/commit/25eb59bfa85b755cd877b02ffda71d0cec70a106))
-* handle training state logical ([#731](https://github.com/microsoft/OCR-Form-Tools/commit/569adf161ad89106ab1fbf51429841c5955e0e4b))
-* fix issue of "After running Layout an all documents FoTT sometimes does not ends" ([#723](https://github.com/microsoft/OCR-Form-Tools/commit/6203e2cd814c95e2bef165f3fb518a566166c26d))
-* set includeTextDetails=true in prebuilt predict ([#722](https://github.com/microsoft/OCR-Form-Tools/commit/ba04cebb63a5b05a369ba954a99dfdc7c9bb9b41))
-* fix issue of "Auto-labeling while switching assets in asset preview causes an error" ([#721](https://github.com/microsoft/OCR-Form-Tools/commit/59fe4e2778a644335da9766fd1382d56086220c1))
-* feat: support api version config ([#717](https://github.com/microsoft/OCR-Form-Tools/commit/c81b2323aaa2b26b0bc0f7922de1e12445fbb627))
-* update homepage style ([#724](https://github.com/microsoft/OCR-Form-Tools/commit/fc769f41c169083098f9250c9ce18ca4881cc336))
-* issuefix: update getBoundingBox ([#730](https://github.com/microsoft/OCR-Form-Tools/commit/f0cb5db337364b2f0355928616d1f7d9637a454a))
-* clone with lodash cloneDeep ([#728](https://github.com/microsoft/OCR-Form-Tools/commit/d6bca5fcf2262a467ede781916a01f50d805b30f))
-* remain auto label state while no label data ([#727](https://github.com/microsoft/OCR-Form-Tools/commit/a79d556a3935e387b0798ecfda8de9c8b1538250))
-* deep copy asset metadata ([#725](https://github.com/microsoft/OCR-Form-Tools/commit/ba8c1100e9adf517e35ec50fd513383d9e84d630))
-* Yongbing chen/receipt predicting ([#626](https://github.com/microsoft/OCR-Form-Tools/commit/e638cd8e3be8926e966a5afc86fb53ac0f092977))
-
-### 2.1-preview.1-32cfaea (11-06-2020)
-* Starain chen/clean autolabel data while training ([#712](https://github.com/microsoft/OCR-Form-Tools/commit/32cfaea023e96c8aa00560a3f30134683ee25757))
-* fix issue of deleting tag ([#703](https://github.com/microsoft/OCR-Form-Tools/commit/282d55700ea9fdf4cac2b0f20901e8ff6115819e))
-
-### 2.1-preview.1-c7ed086 (11-04-2020)
-* Update README.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/c7ed08612876af8bb619a080f6740fceabb4e67c))
-* Update README.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/d696b8a25438590fb44c5159b3142b17178f25d2))
-* fix: use constant if no api version specified ([#684](https://github.com/microsoft/OCR-Form-Tools/commit/8ccdab83f079d976f6521bc08c50d917900483c0))
-* auto labeled tag design & replacing between text with draw region ([#670](https://github.com/microsoft/OCR-Form-Tools/commit/757e0dd85b3c69c6642674e48e9d3549807fecbd))
-
-### 2.1-preview.1-aab6938 (11-03-2020)
-* Fix the issue that git-commit-info.txt could be override ([#683](https://github.com/microsoft/OCR-Form-Tools/commit/aab69380a8e1f7f113011a7c6b6ed406c4329555))
-* fix: use existing git hash when not in git repository ([#682](https://github.com/microsoft/OCR-Form-Tools/commit/586fbb0ce51c27ae42ca857a372e8e8d5dea21d1))
-* Stew ro/use api version selected in project settings ([#678](https://github.com/microsoft/OCR-Form-Tools/commit/bed69a3f64b0da7590ca3c54e8de369844c6bcd9))
-* refactor: change drawn region icon ([#675](https://github.com/microsoft/OCR-Form-Tools/commit/5614da2681bb8fadf9d3db3ff95aa62362d00175))
-
-### 2.1-preview.1-3485d33 (10-30-2020)
-* feat: add bmp support for analyze page ([#672](https://github.com/microsoft/OCR-Form-Tools/commit/3485d33eca96321cf667c5c8eba22cc60af42e23))
-* Alex krasn/bugfix on hotkeys when canvas not loaded yet ([#664](https://github.com/microsoft/OCR-Form-Tools/commit/b0404c6276f8fe55292c929e2ca431ed31ef6442))
-
-### 2.1-preview.1-7166cda (10-29-2020)
-* fix: use node to update status bar with latest git commit ([#671](https://github.com/microsoft/OCR-Form-Tools/commit/7166cdae5763a93feee52842af8e2246fedbf818))
-* change OCR to Layout in UI (Actions) ([#666](https://github.com/microsoft/OCR-Form-Tools/commit/ac604b6bd43eb4c3ba8929a97b308c833d0e6c13))
-* Yongbing chen/hitl update notify message ([#651](https://github.com/microsoft/OCR-Form-Tools/commit/0fa559a4b28c6648eaa17ec047ebb9caabbdc9c7))
-
-### 2.1-preview.1-6d775ae (10-27-2020)
-* Yongbing chen/ui adjustment with designers feedback ([#662](https://github.com/microsoft/OCR-Form-Tools/commit/6d775ae8d4495ca31d110e500b86d3c0eed6a954))
-
-### 2.1-preview.1-c86b6de (10-23-2020)
-* Fix the issue that git-commit-info.txt could be override ([#668](https://github.com/microsoft/OCR-Form-Tools/commit/c86b6de35ecd5d004dfb64f8f857d06f0557a00d))
-* Xinxl/fix hash ([#667](https://github.com/microsoft/OCR-Form-Tools/commit/cb27cbd74ff890dc7e13865d89ca5e16b0807fbb))
-
-### 2.1-preview.1-0aae169 (10-22-2020)
-* Alex krasn/fix confidence level bar styles ([#657](https://github.com/microsoft/OCR-Form-Tools/commit/0aae1690351f3de27114e6cbebd2c077be8e9016))
-
-### 2.1-preview.1-d644459 (10-21-2020)
-* refactor: change error styling and wording for project sharing ([#653](https://github.com/microsoft/OCR-Form-Tools/commit/d644459e4c9b1f82b1ed2d5b537960b0f16184da))
-* fix: sort models after loading next page in model compose ([#659](https://github.com/microsoft/OCR-Form-Tools/commit/9818d6301ef613155951381598f9ad4cf8ff6e3c))
-* Alex krasn/serialize javascript vulnerability ([#612](https://github.com/microsoft/OCR-Form-Tools/commit/66b03303b1325634371ebdb3923acaa6722be89f))
-* update asset labelingState when load local project ([#660](https://github.com/microsoft/OCR-Form-Tools/commit/1aa3daaeeb1c8a4773e7b6236fc6462335e410f9))
-
-### 2.1-preview.1-28c54fc (10-20-2020)
-* fix: check for local connections ([#654](https://github.com/microsoft/OCR-Form-Tools/commit/28c54fcc31defe1c4ebcf685675768b99c8e00c8))
-* get last commit hash code in current branch and show on status bar ([#642](https://github.com/microsoft/OCR-Form-Tools/commit/88c547995d31f945177da70141f997e441b3259c))
-* new feature: tags in current page ([#640](https://github.com/microsoft/OCR-Form-Tools/commit/af5396fe8e63b88b90d16953e17ce2006afe782e))
-
-### 2.1-preview.1-6c1ee2b (10-16-2020)
-* adjust editor view offset ([#646](https://github.com/microsoft/OCR-Form-Tools/commit/6c1ee2b6b4f1bcf28b1c9081b21f0a8783518c80))
-
-### 2.1-preview.1-b92e4b3 (10-15-2020)
-* reword asset states ([#644](https://github.com/microsoft/OCR-Form-Tools/commit/b92e4b3d5a786a852c319c05697eea331c147cee))
-
-### 2.1-preview.1-4544e52 (10-14-2020)
-* feat: support apiVersion selection from project settings ([#641](https://github.com/microsoft/OCR-Form-Tools/commit/4544e5255cf2356a4ddf353f7a63994c1a0865da))
-
-### 2.1-preview.1-94f12bb (10-13-2020)
-* new feature: highlight current tag ([#628](https://github.com/microsoft/OCR-Form-Tools/commit/94f12bb4e925a86fdfba8e25d8b0346169daea1e))
-* new feature: human in the loop auto labeling ([#571](https://github.com/microsoft/OCR-Form-Tools/commit/c1f227daa3decd52320f58d151755b206280cedd))
-
-### 2.1-preview.1-7d1f871 (10-10-2020)
-* Update CHANGELOG.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/7d1f87193b3917f2140ab9bcce04c64e7aceb823))
-
-### 2.1-preview.1-1f33130 (10-09-2020)
-* fix: support image map interactions for container releases([#639](https://github.com/microsoft/OCR-Form-Tools/commit/e015973aee152b8a8b22fc2fe32ce80bdd2b46ea))
-
-### 2.1-preview.1-6d4e93b (10-07-2020)
-* Fix: use file type library for mime type validation ([#636](https://github.com/microsoft/OCR-Form-Tools/commit/6d4e93bca8a4e3d677c765ed5596bde502766e2e))
-
-### 2.1-preview.1-355ca0b (09-30-2020)
-* feat: add spinner in saving project, can avoid multiple commit  ([#617](https://github.com/microsoft/OCR-Form-Tools/commit/355ca0b156b2d44aafd2eaaccf2fc52385c7f5f8))
-
-### 2.1-preview.1-53044f7 (09-29-2020)
-* fix: refresh currentProjects when load project ([#615](https://github.com/microsoft/OCR-Form-Tools/commit/53044f72dd9c9c72557c74c00605ba05ee50205d))
-* sync related region color when tag color changed ([#598](https://github.com/microsoft/OCR-Form-Tools/commit/3044cc51a9166877bb4f01f28753171b82c04ccd))
-* feat: add current list item style ([#601](https://github.com/microsoft/OCR-Form-Tools/commit/3e503e75513e44e6a90bd013d8dd15c3096cd7e9))
-* fix: remove project from app if security token does not exist ([#468](https://github.com/microsoft/OCR-Form-Tools/commit/730e1963a06f038a4efa9750fcef4be6f15a8460))
-
-### 2.1-preview.1-d859d38 (09-27-2020)
-* fix ,update document state when preview (#317) ([#471](https://github.com/microsoft/OCR-Form-Tools/commit/d859d38ecc1f96b194ffa130a1840f5a7d9b1a9b))
-* refactor: change the confidence value format to percentage ([#461](https://github.com/microsoft/OCR-Form-Tools/commit/e806b4e0dfcc68e6408e2130a46a318637a482a8))
-
-### 2.1-preview.1-7a3f7a7 (09-25-2020)
-* security: upgrade node-forge ([#622](https://github.com/microsoft/OCR-Form-Tools/commit/7a3f7a773c8b01f443afaad89d7974a5bbb0b869))
-* fix: disable move tag and support renaming when searching ([#618](https://github.com/microsoft/OCR-Form-Tools/commit/cac1e8e6cfb2805a6540f9e80d564a0ff8be81c7))
-
-### 2.1-preview.1-4163edc (09-23-2020)
-* docs: add latest tag reference to changelog ([#608](https://github.com/microsoft/OCR-Form-Tools/commit/4163edc18bc65234e263703fc829d2f297953385))
-* fix: use region instead of drawnRegion for labelType in label file ([#582](https://github.com/microsoft/OCR-Form-Tools/commit/ffafc200249a1c47698fedb279b4b55cef0190ba))
-* docs: update readme with docker hub info ([#604](https://github.com/microsoft/OCR-Form-Tools/commit/63bbea076d598d0286095fa0eca48d8c9d0ed706))
-* fix: remove opening browser for yarn start ([#605](https://github.com/microsoft/OCR-Form-Tools/commit/f6c4dc3585df71d09252a28f65e835a594389118))
-* fix: update changelog updater script ([#607](https://github.com/microsoft/OCR-Form-Tools/commit/7c4848c3a72259562c0461f0e2eadfb4a660fa64))
-
-### 2.1-preview.1-f2db74e (09-17-2020)
-* docs: udpate changlog with docker image reference ([#590](https://github.com/microsoft/OCR-Form-Tools/commit/f2db74e322c32338eba3b2df06c01a51cfb7ebc1))
-
-### 2.1-preview.1-1a6b78e (09-16-2020)
-* fix: normalize folder path starting with a period ([#592](https://github.com/microsoft/OCR-Form-Tools/commit/1a6b78e054235da3188aafbe65636a8c18b439bf))
-* fix: change label folder uri title ([#588](https://github.com/microsoft/OCR-Form-Tools/commit/7e4233e568d94817e23dda5ef5513b9ee7475d11))
-
-### 2.1-preview.1-6a1ced5 (09-15-2020)
-* fix: initialize drag pan for analyze page ([#586](https://github.com/microsoft/OCR-Form-Tools/commit/6a1ced5a0bfb03ceba515faddbfa010ac8451460))
-* fix: zoomIn keyboar shortcut for macOS ([#581](https://github.com/microsoft/OCR-Form-Tools/commit/5afeebfee28e10e390f073990f90348c5117475f))
-* fix: appId ([#584](https://github.com/microsoft/OCR-Form-Tools/commit/e053b151441e956641ed05c29106d02358a40792))
-* fix: remove escape quote from release script ([#579](https://github.com/microsoft/OCR-Form-Tools/commit/bd5d51e8e15809b95f15bc495f7d0f91fecfc22d))
-* Stew ro/support drag pan for release ([#576](https://github.com/microsoft/OCR-Form-Tools/commit/77620eccd21d564473c81b43341f59de22339248))
-
-### 2.1-preview.1-0633507 (09-14-2020)
-* Update README.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/0633507aa767f996add313ced06c2365c5f240c8))
-
-### 2.1-preview.1-8d2286f (09-13-2020)
-* persist trainPage inputs in localStorage ([#568](https://github.com/microsoft/OCR-Form-Tools/commit/8d2286f50236e41fe5540dbb9b161ea88bbf2d7a))
-
-### 2.1-preview.1-bb23e31 (09-11-2020)
-* build(deps): bump node-fetch from 2.6.0 to 2.6.1 ([#575](https://github.com/microsoft/OCR-Form-Tools/commit/bb23e3199c5721338241c8c5ccc0bda104fd15f8))
-* fix: support multiple env files ([#574](https://github.com/microsoft/OCR-Form-Tools/commit/cf64a8ddde05e7e73cad37d271f5d6dfa61c5d7f))
-* fix: "Azure blob storage"  error on on premise scenario ([#572](https://github.com/microsoft/OCR-Form-Tools/commit/46f0bc59f3a531c366bc2c7cec955d2cb6ed7cd6))
-* fix ([#563](https://github.com/microsoft/OCR-Form-Tools/commit/28c792e10692e3cc1f511852ffc9fdbc8dcdda8a))
-
-### 2.1-preview.1-7e828ff (09-10-2020)
-* fix: allow training with placeholder ([#569](https://github.com/microsoft/OCR-Form-Tools/commit/7e828ff02ff8a22b64b4b7d16787d77afb76af62))
-* docs: update changelog ([#564](https://github.com/microsoft/OCR-Form-Tools/commit/1dec72c5df3206554a1e0864b65cc769835785fd))
-* Yongbing chen/human in the loop ([#517](https://github.com/microsoft/OCR-Form-Tools/commit/be9d56481510e3033bcd705743c1ee9aeee20522))
-* fix: support project folder in project settings for local file system ([#559](https://github.com/microsoft/OCR-Form-Tools/commit/b92b73bb8076f9b9bb55dd38fcd223b7b93eaa2e))
-* feat: enable canvas rotation ([#553](https://github.com/microsoft/OCR-Form-Tools/commit/c27a110251df1fc7a595524846e20fd09c79f915))
-* fix: handle tag is undefined error ([#557](https://github.com/microsoft/OCR-Form-Tools/commit/7e4d3fbbc3a2bf925c126cd2b3f493cca48e7a62))
-
-### 2.1-preview.1-193520e (09-08-2020)
-* fix: accept selection of only .fott files for open local project ([#554](https://github.com/microsoft/OCR-Form-Tools/commit/193520e2c3e40b58c3612507efc2249aaf4e9d05))
-* fix: use default shared folder for label URI when training ([#551](https://github.com/microsoft/OCR-Form-Tools/commit/656de2ff07c2083affc2adf52f1a56c5a9c024b8))
-* fix: show label folder uri while training ([#539](https://github.com/microsoft/OCR-Form-Tools/commit/0ad389c06328cd6428653bb7d94d5af716e02ab7))
-* feat: add canvas command bar to analyze page with only zoom buttons ([#549](https://github.com/microsoft/OCR-Form-Tools/commit/895b52740cd1e8d61b5b021e6c0d992f44ce8052))
-
- ### 2.1-preview.1-4852c84 (09-05-2020)
-* fix buttons styles - makes them more visible ([#526](https://github.com/microsoft/OCR-Form-Tools/commit/4852c8429d25b5569c3335b014da5972cbcc6162))
-
-### 2.1-preview.1-343ea16 (09-04-2020)
-* refactor: remove array for drawn region labels ([#542](https://github.com/microsoft/OCR-Form-Tools/commit/343ea16e18199ab5098395ae8b7a164cd8bab55e))
-* fix: add key prop to region icon ([#540](https://github.com/microsoft/OCR-Form-Tools/commit/87b69093f2d35d91a2a939c46ac66ba4d22a5cb7))
-
-### 2.1-preview.1-b370c9a (09-02-2020)
-* fix: resize canvas on asset preview resize ([#535](https://github.com/microsoft/OCR-Form-Tools/commit/b370c9a9bcf7da416944c626f6d4fd7bd29088bb))
-
-### 2.1-preview.1-de1c304 (08-31-2020)
-* refactor: upgrade tsconfig es2017 to esnext ([#531](https://github.com/microsoft/OCR-Form-Tools/commit/de1c30410b860c9576108f076ec4dd8273e61a79))
-
-### 2.1-preview.1-530545c (08-28-2020)
-* fix: remove existing bounding boxes from document on analyze ([#523](https://github.com/microsoft/OCR-Form-Tools/commit/6a1aedfb89b0499a0f4782e16ccbd8a06887841d))
-* feat: enable download JSON of trained model ([#513](https://github.com/microsoft/OCR-Form-Tools/commits/master)) 
-
-### 2.1-preview.1-529a0e8 (08-27-2020)
-* fix: show loading indicator while loading model info ([#514](https://github.com/microsoft/OCR-Form-Tools/commit/529a0e819f4cb405e290f34d18d15c487a7bcfad))
-* docs: update telemetry disclaimer ([#521](https://github.com/microsoft/OCR-Form-Tools/pull/521))
-* fix: disable clearing of drawn regions on analyze page ([#518](https://github.com/microsoft/OCR-Form-Tools/commit/298d7c97da1278996d2ee6020d3face0785bc4eb))
-
-### 2.1-preview.1-b2d9a0b (08-26-2020)
-* docs: notice that telemetry is disabled ([#501](https://github.com/microsoft/OCR-Form-Tools/commit/b2d9a0b008ebf350dfcb5fe897fc5dfe0d4d5cb6))
-
-### 2.1-preview.1-d9db4ee (08-24-2020)
-* refactor: upgrade storage-blob to v12.1.2 ([#509](https://github.com/microsoft/OCR-Form-Tools/commit/d9db4ee027240a82feef5b54e5e406c3793d8050))
-* feat: support region labeling ([#481](https://github.com/microsoft/OCR-Form-Tools/commit/dd78ed06761a341908bdb1b09e73fd1f2868431c))
-* feat: support adding model to recent models from compose page ([#510](https://github.com/microsoft/OCR-Form-Tools/commit/65fc92b5737ceea14ff89aa78052be26835ad0ae))
-
-### 2.1-preview.1-2402cba (08-17-2020)
-* fix: notify error message when open project with invalid security token ([#506](https://github.com/microsoft/OCR-Form-Tools/commit/2402cbaf73eba47ad188f851227c04cd44a208d4))
-
-### 2.1-preview.1-a8ef8fa (08-17-2020)
-* fix: don't allow create or update connection with duplicate name ([#486](https://github.com/microsoft/OCR-Form-Tools/commit/a8ef8fab603b3d2c08c533cb5dfe67da117942a0))
-
-### 2.1-preview.1-530545c (08-14-2020)
-* fix: "failed to fetch()" error ([#491](https://github.com/microsoft/OCR-Form-Tools/commit/530545c7cd2b4a3ff444e9c7e1f40c68d4a7376c))
-* fix: sync layer visibility ([#497](https://github.com/microsoft/OCR-Form-Tools/commit/bea552b28acb9b652ffaedf40009d6df5a3197ef))
-* refactor: disable telemetry service ([#498](https://github.com/microsoft/OCR-Form-Tools/commit/6e3628cf174f954693380aab6ebd2dabe027ac6d))
-* fix: change share class name for adblocker chrome extension ([#492](https://github.com/microsoft/OCR-Form-Tools/commit/aa8a73afc6344f3164e79f236d5fa4bb0f64d364))
-
-### 2.1-preview.1-da405b3 (08-10-2020)
-* fix: restrict tag type through hot keys ([#482](https://github.com/microsoft/OCR-Form-Tools/commit/da405b354428b829e895a35a020736b1d88c153f))
-* docs: add share project description to README ([#488](https://github.com/microsoft/OCR-Form-Tools/commit/7ee215f735a84aaa30201748d19207bcc6a05580))
-
-### 2.1-preview.1-29d1f93 (08-07-2020)
-* fix: handle multi selection of non-compatible types with multi-selection tool ([#487](https://github.com/microsoft/OCR-Form-Tools/commit/29d1f93a290e55fdd84f8cf2ee9a914fed702beb))
-
-### 2.1-preview.1-cef225f (08-06-2020)
-* fix: handle undefined image map error ([#462](https://github.com/microsoft/OCR-Form-Tools/commit/cc9e9bfc8fe00bb0ed154edb791446f28060af4e))
-* fix: handle undefined image map error ([#479](https://github.com/microsoft/OCR-Form-Tools/commit/cef225f3346628e79c46e799303400965f1d3c96))
-
-### 2.1-preview.1-76945df (08-05-2020)
-* fix: use english for telemetry reporting ([#472](https://github.com/microsoft/OCR-Form-Tools/commit/76945df3bdf9caba3ba13f4541e17e75b9574b33))
-* fix: resolve unhandled exeptions and new message for OCR service on 400 ([#470](https://github.com/microsoft/OCR-Form-Tools/commit/76381bc659a365ead19387b933485530d2d5edc3))
-* feature: enable popup with composed model info ([#460](https://github.com/microsoft/OCR-Form-Tools/commit/c1f5d803f047e5ca0d18fea6383b3baf56d116ff))
-
-### 2.1-preview.1-f4d53ce (08-03-2020)
-* fix: bump elliptic from 6.5.2 to 6.5.3 ([#469](https://github.com/microsoft/OCR-Form-Tools/commit/f4d53cec967194445885bd3748096f0a3ce10715))
-* feat: add modelCompose icon and created time ([#466](https://github.com/microsoft/OCR-Form-Tools/commit/2fa32ef5f77ec7bb44bf42e9fc0a5fdf7f0330c3))
-
-### 2.1-preview.1-78996ea (07-31-2020)
-* refactor: relocate share button ([#464](https://github.com/microsoft/OCR-Form-Tools/commit/78996ea65616b28d7471b59f4f16f254d7d33127))
-
-### 2.1-preview.1-0e1b637 (07-29-2020)
-* feat: show only ready models in the list ([#459](https://github.com/microsoft/OCR-Form-Tools/commit/0e1b637003f289c56955342f44963003c1543436))
-
-### 2.1-preview.1-84f8285 (07-27-2020)
-* fix: show message on model composition fail ([#457](https://github.com/microsoft/OCR-Form-Tools/commit/84f82859122ff298bcfcca78e821e8bfe437bb78))
-* refactor: add background on popup table ([#446](https://github.com/microsoft/OCR-Form-Tools/commit/27f60df5617da2efba8ffdd601233e0c0f4c8e3e))
-
-### 2.1-preview.1-79264e3 (07-24-2020)
-* fix: handle rejection for security token not found when opening projects ([#441](https://github.com/microsoft/OCR-Form-Tools/commit/79264e3fddfb2c80b88bf8ca21df1e869082ffcf))
-* fix: show more refined error message for model not found analysis error ([#454](https://github.com/microsoft/OCR-Form-Tools/commit/1cb4133dca0092559e7524dfad8c0bf54502dc81))
-* feat: support group selection of words with drawn bounding box ([#447](https://github.com/microsoft/OCR-Form-Tools/commit/b4332a926b1925024a33731a90d303c0b171935b))
-* feat: add apiVersion to telemetry ([#448](https://github.com/microsoft/OCR-Form-Tools/commit/55be5427e4a2f9c8cf393d446049527c55f841d4))
-* fix: margin for filenames in asset preview ([#451](https://github.com/microsoft/OCR-Form-Tools/commit/fe8258f9c7ceba663a66708b19bc0e6556e777ad))
-* docs: add telemetry disclaimer to readme ([#449](https://github.com/microsoft/OCR-Form-Tools/commit/87356a1cf6678bb9494e83178bf6282ca366921f))
-
-### 2.1-preview.1-9b5b99d (07-23-2020)
-* docs: add get-sas.png (https://github.com/microsoft/OCR-Form-Tools/commit/9b5b99d5468661481ae8165593d5a74471366429)
-* doc: add a screenshot of getting SAS token (https://github.com/microsoft/OCR-Form-Tools/commit/87b1062125ed106ff73c036e33f1bf7a5f2c3def)
-* fix: handle undefined error for pdf asset preview memory cleaning ([#442](https://github.com/microsoft/OCR-Form-Tools/commit/9b5b99d5468661481ae8165593d5a74471366429))
-* fix: remove duplicate models in model composed model list ([#439](https://github.com/microsoft/OCR-Form-Tools/commit/7fcc9ccfdb6634326ddd6cbfe99b423300b94131))
-* feat: enable internal telemetry ([#431](https://github.com/microsoft/OCR-Form-Tools/commit/41294c8aa19c82643fe0df669c21a0112668e0dd))
-
-### 2.1-preview.1-f4b4d5d (07-21-2020)
-* fix: use table for model selection info ([#438](https://github.com/microsoft/OCR-Form-Tools/commit/f4b4d5ded4b7e0ff2116ba3b8f97e49fbf30b7c0))
-* fix: reset model name after training ([#434](https://github.com/microsoft/OCR-Form-Tools/commit/ed919a016b150d0938aee25b5550bacf29f04e83))
-* fix: wait for loadeding project with sharing project ([#435](https://github.com/microsoft/OCR-Form-Tools/commit/fc4cb96d2a9d0920c3bbbd9c2000fb4b1b7ac9c0))
-
-### 2.1-preview.1-46dbb2b (07-20-2020)
-* fix: handle no recent models for model compose ([#432](https://github.com/microsoft/OCR-Form-Tools/commit/46dbb2be9ee6100a8f3e6a443ad5e734c60954bb))
-* refactor: use new model compose icon ([#425](https://github.com/microsoft/OCR-Form-Tools/commit/932fb3fd7f84636e97035f4cafadc87cff18b3b3))
-* fix: support long model names for model selection ([#427](https://github.com/microsoft/OCR-Form-Tools/commit/a0fa2daf4cd3286f7f58dc2919fd202115e8d5be))
-* feat add recent models to top of model compose page's list ([#430](https://github.com/microsoft/OCR-Form-Tools/commit/cf8de6be61b95bfe8c937946df71ea81aecb35f9))
-* fix: check valid connection ([#428](https://github.com/microsoft/OCR-Form-Tools/commit/9cb6c5830afddc9317ffdfe6927b581c4d39ba39))
-
-### 2.1-preview.1-162a766 (07-17-2020)
-* refactor: make confidence results same as JSON results ([#409](https://github.com/microsoft/OCR-Form-Tools/commit/162a7660cfe32b72c4954a147269c5d2b7f55a08))
-* fix: prevent user from leaving page while composing ([#422](https://github.com/microsoft/OCR-Form-Tools/commit/63e179d0152d2f8f2ee764443785efa24e5f7dce))
-* feat: support model selection ([#419](https://github.com/microsoft/OCR-Form-Tools/commit/b4c4cc5a8a980aaa6530e7a4a5a1c43e77494c75))
-* feat: share project ([#344](https://github.com/microsoft/OCR-Form-Tools/commit/d059580cfefa053670c45c5d8ec7bf250bc4db27))
-
-### 2.1-preview.1-89be3ac (07-15-2020)
-* fix: on assetFormat undefined ([#413](https://github.com/microsoft/OCR-Form-Tools/commit/89be3ac5b614e91607d7fb8065ad32b69886040d))
-* fix: make sure token names are unique ([#404](https://github.com/microsoft/OCR-Form-Tools/commit/d8fa6141cff4d00ba22e95ef4f5dcc9102e1c1c2))
-* fix: model info enclosing element error on [#407](https://github.com/microsoft/OCR-Form-Tools/issues/407) ([#408](https://github.com/microsoft/OCR-Form-Tools/commit/8cc421c3fee0e781211efb0aeb2b345075012daa))
-* fix: display composed icon for composed model with attribute ([#399](https://github.com/microsoft/OCR-Form-Tools/commit/18fb4d71052b9355c8d5a4f7dde956ba17ca30fa))
-
-### 2.1-preview.1-b67191c (07-09-2020)
-* fix: don't allow choosing not-ready models for compose ([#394](https://github.com/microsoft/OCR-Form-Tools/commit/b67191cdbc872b9004be30aa4b4dfde9a88dfe37))
-* feat: track five most recent project models ([#395](https://github.com/microsoft/OCR-Form-Tools/commit/05850603d51a6786c8b6e8b4a553db020df56158))
-
-### 2.1-preview.1-abc6376 (07-08-2020)
-* feat: enable model info in analyze results ([#383](https://github.com/microsoft/OCR-Form-Tools/commit/abc63767e97dd28a6bb9028e03f2225e6ac0f1ab))
-* fix: check invalid provider options before project actions ([#390](https://github.com/microsoft/OCR-Form-Tools/commit/212647d4327d9e18e9248a2d39086eeaab404979))
-
-### 2.1-preview.1-a334cfc (07-07-2020)
-* fix: hide extra scrollbars for model compose view ([#380](https://github.com/microsoft/OCR-Form-Tools/commit/a334cfc45fc5ab137682ad2b48dd0ec1585055dc))
-* fix: handle version change state mutation error ([#382](https://github.com/microsoft/OCR-Form-Tools/commit/8991cc0c92f2f5cbd226f7e1c5c0825b7af8937c))
-* fix: handle pdf worker terminated error ([#381](https://github.com/microsoft/OCR-Form-Tools/commit/adc0498c31bfd5ba57ab98c373e73575589ab1e1)) 
-
-### 2.1-preview.1-7192170 (07-02-2020)
-* feat: support release ([#361](https://github.com/microsoft/OCR-Form-Tools/commit/7192170d73d24a43e7fff18cd2c6bae7f208f1b0))
-
-### 2.1-preview.1-978dabc (07-01-2020)
-* feat: support document management ([#374](https://github.com/microsoft/OCR-Form-Tools/commit/978dabc3ba877ed4215865cba2a583fb785a2894))
-
-### 2.1-preview.1-56a4b89 (06-30-2020)
-* fix: wait until composed model is ready ([#369](https://github.com/microsoft/OCR-Form-Tools/commit/56a4b89f370f2fd72c6bc275376205e7fffe6a9e))
-
-### 2.1-preview.1-6114d64 (06-23-2020)
-* fix: update OCR version ([#335](https://github.com/microsoft/OCR-Form-Tools/commit/6114d6456b27a59335e534eef72cefd1b2f15737))
-* feat: support electron for on premise solution ([#333](https://github.com/microsoft/OCR-Form-Tools/commit/ca0bd0c2ab46b7b587e5bfbc60c29b62bb325297))
-
-### 2.1-preview.1-8297b18 (06-19-2020)
-* refactor: put api version in constants ([#332](https://github.com/microsoft/OCR-Form-Tools/commit/8297b18a084be86bc4c986a1a332cb40bd807d1b))
-
-### 2.1-preview.1-3b7f803 (06-18-2020)
-* feat: enable model compose (preview) ([#328](https://github.com/microsoft/OCR-Form-Tools/commit/3b7f803407b82191706120bb9f12b82de1955704))
-* fix: quick reordering tags ([#322](https://github.com/microsoft/OCR-Form-Tools/commit/3cc5267ef8617590adb3d4966f75cfed64604f00))
-* feat: localization for canvas commandbar items ([#319](https://github.com/microsoft/OCR-Form-Tools/commit/253b9c90eb4923e7fde015a7216905fa32a8dcfa))
-* feat: enable re-run OCR ([#297](https://github.com/microsoft/OCR-Form-Tools/commit/cbe9b0ed1c48f54c100b31b7f04706a969df2dd5))
-* fix: capitalize python in analyze page ([#320](https://github.com/microsoft/OCR-Form-Tools/commit/96626636a96a3d19030df283ac794fa9c2aab18c))
-* fix: fix spelling correction for string match ([#318](https://github.com/microsoft/OCR-Form-Tools/commit/28e53cefcf0bb462d547d6e38b24c480c03b946f))
-* feature: keep prediction in UI ([#285](https://github.com/microsoft/OCR-Form-Tools/commit/dad98b9bd1d305a6bfeb2846ef4067da186ff801))
-
-### 2.0.0-1c39800 (06-05-2020)
-* feat: add description - how to delete info ([#292](https://github.com/microsoft/OCR-Form-Tools/commit/1c39800b1152f186dfc19834bb969abbc4fe0ac2))
-* feat: enable download analyze script ([#304](https://github.com/microsoft/OCR-Form-Tools/commit/9c97ed0ff9b0aa72ec9a197fc92f3a5998135c36))
-* fix: check ocrread results before getting image extent ([#296](https://github.com/microsoft/OCR-Form-Tools/commit/61dba02fc6f19eb854e1f499e475b1336e6171b9))
-* feat: Add better error message for CORS ([#289](https://github.com/microsoft/OCR-Form-Tools/commit/8f210792b4d84e424b00499efb540b0e27e9fdad))
-
-### 2.0.0-2760166 (05-30-2020)
-* fix: fix mime check bug for jpeg/jpg and tiff ([#291](https://github.com/microsoft/OCR-Form-Tools/commit/2760166bcb809bbfdc207b01db49f00153318624))
-* refactor: simplify shortcut descriptions ([#277](https://github.com/microsoft/OCR-Form-Tools/commit/db95b0e2510f6cef9bc7279fe0a19dce239c816e))
-
-### 2.0.0-a5e4e07 (05-21-2020)
-* feature: show table view when table icon is clicked ([#271](https://github.com/microsoft/OCR-Form-Tools/commit/a5e4e079d4c0d1c7c52e3b015c0ddf9b8601bbf2))
-
-### 2.0.0-814276a (05-20-2020)
-* fix: modify skip button according to feedback comments ([#264](https://github.com/microsoft/OCR-Form-Tools/commit/814276af6f4259844854798adf0c56bd606b2363))
-* feature: keyboard shortcuts and tips ([#258](https://github.com/microsoft/OCR-Form-Tools/commit/37aa859a80dc0213a118313558ad21ba424008e7))
-* feat: add electron mode from VoTT project ([#260](https://github.com/microsoft/OCR-Form-Tools/commit/2a3383d4a0f100a39ed40627bdffb9b48f78f5df))
-* refactor: use forEach instead of map in handleFeatureSelect ([#259](https://github.com/microsoft/OCR-Form-Tools/commit/c1c590c463743d187fda2429a628e27c6c42012f))
-
-### 2.0.0-0061645 (05-13-2020)
-* build: update nginx base image version to 1.18.0-alpine ([#255](https://github.com/microsoft/OCR-Form-Tools/commit/0061645871806595e4fe2ab5991cc494afa26b31))
-* fix: assign empty string when predict item's fieldName is undefined ([#254](https://github.com/microsoft/OCR-Form-Tools/commit/d4d919f678b1f162f48c87ee5223281e57945a0a))
-* fix: overlaping left split pane ([#252](https://github.com/microsoft/OCR-Form-Tools/commit/2e8c351f74c385b8627ee6ea39f974e5e048ea8d))
-* refactor: change predict to analyze in UI while keeping predict term ([#147](https://github.com/microsoft/OCR-Form-Tools/commit/c9aa58e36a10a35083249a8080c2cfb9fccf3733))
-### 2.0.0-7c7ba93 (05-07-2020)
-* fix: check null value from post processed value ([#248](https://github.com/microsoft/OCR-Form-Tools/commit/a361189c527bfffd6417f90a2521ad40b2b3f205))
-* feat: enable outputting to file for analyze script ([#246](https://github.com/microsoft/OCR-Form-Tools/commit/7c7ba937f140490775b788d63ef2c7ed63ca40f1))
-### 2.0.0-9d91800 (05-06-2020)
-* fix: prevent user from changing tag types when invalid ([#224](https://github.com/microsoft/OCR-Form-Tools/commit/d8823a33591db5c5dc9a0af753e007167218a3e3))
-* fix: prevent user from adding multiple checkboxes to a single tag ([#224](https://github.com/microsoft/OCR-Form-Tools/commit/d8823a33591db5c5dc9a0af753e007167218a3e3))
-* fix: display error when inputted SAS doesn't contain token ([#243](https://github.com/microsoft/OCR-Form-Tools/commit/9826ca8504549f23057c9cad1baebc5e9d1f6fe7))
-### 2.0.0-25d3298 (05-04-2020)
-* feat: track document count for tags ([#231](https://github.com/microsoft/OCR-Form-Tools/commit/70a6e43dc54239cdc153d5d328b17c1dfa0f085f))
-* fix: display error when inputted service URI contains path or query ([#234](https://github.com/microsoft/OCR-Form-Tools/commit/04a16961b37ad5b5d01fc4c93addaaf69cbf0e72))
-* feat: add link in status bar to CHANGELOG ([#233](https://github.com/microsoft/OCR-Form-Tools/commit/e66646a13263239213580378bbd2d8462d7e22b6))
-### 2.0.0-f6c8ffa (05-01-2020)
-* refactor: change checkbox to selectionMark ([#223](https://github.com/microsoft/OCR-Form-Tools/commit/f6c8ffad6edf23f6241f314e9456da92bc1a8402))
-### 2.0.0-f3e42f6 (04-30-2020)
-* feat: display post-processed value in analyzed results ([#229](https://github.com/microsoft/OCR-Form-Tools/commit/f3e42f6e8e9e934f1a241921dbe4a1e8d311bb46))
-### 2.0.0-f068866 (04-28-2020)
-* fix: hide sprin in tag input control when open an empty folder ([#220](https://github.com/microsoft/OCR-Form-Tools/commit/f0688668df2e676fce9749fad8ec9d39e56697cf))
-* perf: cache images, reduce canvas size, and fix memory leak for asset preview ([#218](https://github.com/microsoft/OCR-Form-Tools/commit/e8ad9a3bebf2a1ae210e0e1fa3eebba564592c4c))
-### 2.0.0-595a512 (04-24-2020)
-* fix: align rotated picture asset with OCR result
-### 2.0.0-51c02cc (04-20-2020)
-* fix: scrollbar fix when page size changes
-* fix: Add split pane to fix too long tag name is invisible in right sidebar
-### 2.0.0-202fb2f (04-20-2020)
-* perf: improve assets loading performance and fix some bugs
-### 2.0.0-bce554e (04-16-2020)
-* perf: improve Azure Blob file list performance
-* feat: support URL upload for predicting file
-### 2.0.0-ef18425 (04-09-2020)
-* feat: enable checkbox labeling (preview)
+# FoTT Changelog
+## What's new in Form Recognizer?
+Click [here](https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/whats-new) to see what's new in Form Recognizer.
+
+## Released conatiner's currently referenced commit
+2.1-Preview's released container image, tracked by the `latest-preview` image tag in our [docker hub repository](https://hub.docker.com/_/microsoft-azure-cognitive-services-custom-form-labeltool), currently references **2.1-preview.1-1f33130 (10-09-2020)**
+
+## Commit history
+### 2.1-preview.2-b6b9a2f (12-10-2020)
+* update appVersion to 2.1.2 ([#808](https://github.com/microsoft/OCR-Form-Tools/commit/b6b9a2f131485d08541a1e85f6af59ebfbeca773))
+* add locale in prebuiltPredictPage ([#772](https://github.com/microsoft/OCR-Form-Tools/commit/06d9c16a7c1fe64a95d835878dcb8dabb8c7e485)) ([#776](https://github.com/microsoft/OCR-Form-Tools/commit/06d9c16a7c1fe64a95d835878dcb8dabb8c7e485))
+* Stew ro/cherry pick 347e21e 2b6ead7 ([#766](https://github.com/microsoft/OCR-Form-Tools/commit/ca59cee26587e1aee49507abd75c0683d67f541f))
+* Cherry pick 34ce14d a7ccb34 ([#763](https://github.com/microsoft/OCR-Form-Tools/commit/244c23df700791794990eb6f7e196bcf9ff9c844))
+* Stew ro/cherry pick ab5a8a8 abfffbb ([#760](https://github.com/microsoft/OCR-Form-Tools/commit/93b7a2d4d7688cda4baa4cfd704b2666df524174))
+* refactor: disable api version selection ([#755](https://github.com/microsoft/OCR-Form-Tools/commit/be1f18db0b7073dad106f443f343aa221dea7fc6))
+* refactor: disable draw region button ([#756](https://github.com/microsoft/OCR-Form-Tools/commit/8816a85761a795f3f0b34b87360236cadd80a735))
+* feat: support null text values in analyze results ([#744](https://github.com/microsoft/OCR-Form-Tools/commit/0ddb7f1275d6f195c2af9f0b7053987e01a5d677))
+* feat: support rowspan and column span for layout tables ([#754](https://github.com/microsoft/OCR-Form-Tools/commit/6994ac929146e25370d1461e4e502773de3d5503))
+* Update changelog ([#750](https://github.com/microsoft/OCR-Form-Tools/commit/acba3966ce960e474dfd3d97510b07c108e7b39f))
+* check whether the label data is null ([#753](https://github.com/microsoft/OCR-Form-Tools/commit/f6ef41ac1500e52f3714eda6e99187e016e1b223))
+* fix issue of 773 ([#740](https://github.com/microsoft/OCR-Form-Tools/commit/9d35e79393cdb0de678e9ec6b850daf5a4df5c96))
+
+### 2.1-preview.1-2e50498 (11-09-2020)
+* fix: enable api version selection ([#736](https://github.com/microsoft/OCR-Form-Tools/commit/2e5049883bd1550ba80210edca7db4233d7a15fa))
+* fix: labeling doesn't work via shortcuts on the new project or empty tags ([#677](https://github.com/microsoft/OCR-Form-Tools/commit/f11291940b776ceb8ba7708e6f58dc2572f7b01b))
+* fix: remove setting project state in project form on change ([#732](https://github.com/microsoft/OCR-Form-Tools/commit/25eb59bfa85b755cd877b02ffda71d0cec70a106))
+* handle training state logical ([#731](https://github.com/microsoft/OCR-Form-Tools/commit/569adf161ad89106ab1fbf51429841c5955e0e4b))
+* fix issue of "After running Layout an all documents FoTT sometimes does not ends" ([#723](https://github.com/microsoft/OCR-Form-Tools/commit/6203e2cd814c95e2bef165f3fb518a566166c26d))
+* set includeTextDetails=true in prebuilt predict ([#722](https://github.com/microsoft/OCR-Form-Tools/commit/ba04cebb63a5b05a369ba954a99dfdc7c9bb9b41))
+* fix issue of "Auto-labeling while switching assets in asset preview causes an error" ([#721](https://github.com/microsoft/OCR-Form-Tools/commit/59fe4e2778a644335da9766fd1382d56086220c1))
+* feat: support api version config ([#717](https://github.com/microsoft/OCR-Form-Tools/commit/c81b2323aaa2b26b0bc0f7922de1e12445fbb627))
+* update homepage style ([#724](https://github.com/microsoft/OCR-Form-Tools/commit/fc769f41c169083098f9250c9ce18ca4881cc336))
+* issuefix: update getBoundingBox ([#730](https://github.com/microsoft/OCR-Form-Tools/commit/f0cb5db337364b2f0355928616d1f7d9637a454a))
+* clone with lodash cloneDeep ([#728](https://github.com/microsoft/OCR-Form-Tools/commit/d6bca5fcf2262a467ede781916a01f50d805b30f))
+* remain auto label state while no label data ([#727](https://github.com/microsoft/OCR-Form-Tools/commit/a79d556a3935e387b0798ecfda8de9c8b1538250))
+* deep copy asset metadata ([#725](https://github.com/microsoft/OCR-Form-Tools/commit/ba8c1100e9adf517e35ec50fd513383d9e84d630))
+* Yongbing chen/receipt predicting ([#626](https://github.com/microsoft/OCR-Form-Tools/commit/e638cd8e3be8926e966a5afc86fb53ac0f092977))
+
+### 2.1-preview.1-32cfaea (11-06-2020)
+* Starain chen/clean autolabel data while training ([#712](https://github.com/microsoft/OCR-Form-Tools/commit/32cfaea023e96c8aa00560a3f30134683ee25757))
+* fix issue of deleting tag ([#703](https://github.com/microsoft/OCR-Form-Tools/commit/282d55700ea9fdf4cac2b0f20901e8ff6115819e))
+
+### 2.1-preview.1-c7ed086 (11-04-2020)
+* Update README.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/c7ed08612876af8bb619a080f6740fceabb4e67c))
+* Update README.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/d696b8a25438590fb44c5159b3142b17178f25d2))
+* fix: use constant if no api version specified ([#684](https://github.com/microsoft/OCR-Form-Tools/commit/8ccdab83f079d976f6521bc08c50d917900483c0))
+* auto labeled tag design & replacing between text with draw region ([#670](https://github.com/microsoft/OCR-Form-Tools/commit/757e0dd85b3c69c6642674e48e9d3549807fecbd))
+
+### 2.1-preview.1-aab6938 (11-03-2020)
+* Fix the issue that git-commit-info.txt could be override ([#683](https://github.com/microsoft/OCR-Form-Tools/commit/aab69380a8e1f7f113011a7c6b6ed406c4329555))
+* fix: use existing git hash when not in git repository ([#682](https://github.com/microsoft/OCR-Form-Tools/commit/586fbb0ce51c27ae42ca857a372e8e8d5dea21d1))
+* Stew ro/use api version selected in project settings ([#678](https://github.com/microsoft/OCR-Form-Tools/commit/bed69a3f64b0da7590ca3c54e8de369844c6bcd9))
+* refactor: change drawn region icon ([#675](https://github.com/microsoft/OCR-Form-Tools/commit/5614da2681bb8fadf9d3db3ff95aa62362d00175))
+
+### 2.1-preview.1-3485d33 (10-30-2020)
+* feat: add bmp support for analyze page ([#672](https://github.com/microsoft/OCR-Form-Tools/commit/3485d33eca96321cf667c5c8eba22cc60af42e23))
+* Alex krasn/bugfix on hotkeys when canvas not loaded yet ([#664](https://github.com/microsoft/OCR-Form-Tools/commit/b0404c6276f8fe55292c929e2ca431ed31ef6442))
+
+### 2.1-preview.1-7166cda (10-29-2020)
+* fix: use node to update status bar with latest git commit ([#671](https://github.com/microsoft/OCR-Form-Tools/commit/7166cdae5763a93feee52842af8e2246fedbf818))
+* change OCR to Layout in UI (Actions) ([#666](https://github.com/microsoft/OCR-Form-Tools/commit/ac604b6bd43eb4c3ba8929a97b308c833d0e6c13))
+* Yongbing chen/hitl update notify message ([#651](https://github.com/microsoft/OCR-Form-Tools/commit/0fa559a4b28c6648eaa17ec047ebb9caabbdc9c7))
+
+### 2.1-preview.1-6d775ae (10-27-2020)
+* Yongbing chen/ui adjustment with designers feedback ([#662](https://github.com/microsoft/OCR-Form-Tools/commit/6d775ae8d4495ca31d110e500b86d3c0eed6a954))
+
+### 2.1-preview.1-c86b6de (10-23-2020)
+* Fix the issue that git-commit-info.txt could be override ([#668](https://github.com/microsoft/OCR-Form-Tools/commit/c86b6de35ecd5d004dfb64f8f857d06f0557a00d))
+* Xinxl/fix hash ([#667](https://github.com/microsoft/OCR-Form-Tools/commit/cb27cbd74ff890dc7e13865d89ca5e16b0807fbb))
+
+### 2.1-preview.1-0aae169 (10-22-2020)
+* Alex krasn/fix confidence level bar styles ([#657](https://github.com/microsoft/OCR-Form-Tools/commit/0aae1690351f3de27114e6cbebd2c077be8e9016))
+
+### 2.1-preview.1-d644459 (10-21-2020)
+* refactor: change error styling and wording for project sharing ([#653](https://github.com/microsoft/OCR-Form-Tools/commit/d644459e4c9b1f82b1ed2d5b537960b0f16184da))
+* fix: sort models after loading next page in model compose ([#659](https://github.com/microsoft/OCR-Form-Tools/commit/9818d6301ef613155951381598f9ad4cf8ff6e3c))
+* Alex krasn/serialize javascript vulnerability ([#612](https://github.com/microsoft/OCR-Form-Tools/commit/66b03303b1325634371ebdb3923acaa6722be89f))
+* update asset labelingState when load local project ([#660](https://github.com/microsoft/OCR-Form-Tools/commit/1aa3daaeeb1c8a4773e7b6236fc6462335e410f9))
+
+### 2.1-preview.1-28c54fc (10-20-2020)
+* fix: check for local connections ([#654](https://github.com/microsoft/OCR-Form-Tools/commit/28c54fcc31defe1c4ebcf685675768b99c8e00c8))
+* get last commit hash code in current branch and show on status bar ([#642](https://github.com/microsoft/OCR-Form-Tools/commit/88c547995d31f945177da70141f997e441b3259c))
+* new feature: tags in current page ([#640](https://github.com/microsoft/OCR-Form-Tools/commit/af5396fe8e63b88b90d16953e17ce2006afe782e))
+
+### 2.1-preview.1-6c1ee2b (10-16-2020)
+* adjust editor view offset ([#646](https://github.com/microsoft/OCR-Form-Tools/commit/6c1ee2b6b4f1bcf28b1c9081b21f0a8783518c80))
+
+### 2.1-preview.1-b92e4b3 (10-15-2020)
+* reword asset states ([#644](https://github.com/microsoft/OCR-Form-Tools/commit/b92e4b3d5a786a852c319c05697eea331c147cee))
+
+### 2.1-preview.1-4544e52 (10-14-2020)
+* feat: support apiVersion selection from project settings ([#641](https://github.com/microsoft/OCR-Form-Tools/commit/4544e5255cf2356a4ddf353f7a63994c1a0865da))
+
+### 2.1-preview.1-94f12bb (10-13-2020)
+* new feature: highlight current tag ([#628](https://github.com/microsoft/OCR-Form-Tools/commit/94f12bb4e925a86fdfba8e25d8b0346169daea1e))
+* new feature: human in the loop auto labeling ([#571](https://github.com/microsoft/OCR-Form-Tools/commit/c1f227daa3decd52320f58d151755b206280cedd))
+
+### 2.1-preview.1-7d1f871 (10-10-2020)
+* Update CHANGELOG.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/7d1f87193b3917f2140ab9bcce04c64e7aceb823))
+
+### 2.1-preview.1-1f33130 (10-09-2020)
+* fix: support image map interactions for container releases([#639](https://github.com/microsoft/OCR-Form-Tools/commit/e015973aee152b8a8b22fc2fe32ce80bdd2b46ea))
+
+### 2.1-preview.1-6d4e93b (10-07-2020)
+* Fix: use file type library for mime type validation ([#636](https://github.com/microsoft/OCR-Form-Tools/commit/6d4e93bca8a4e3d677c765ed5596bde502766e2e))
+
+### 2.1-preview.1-355ca0b (09-30-2020)
+* feat: add spinner in saving project, can avoid multiple commit  ([#617](https://github.com/microsoft/OCR-Form-Tools/commit/355ca0b156b2d44aafd2eaaccf2fc52385c7f5f8))
+
+### 2.1-preview.1-53044f7 (09-29-2020)
+* fix: refresh currentProjects when load project ([#615](https://github.com/microsoft/OCR-Form-Tools/commit/53044f72dd9c9c72557c74c00605ba05ee50205d))
+* sync related region color when tag color changed ([#598](https://github.com/microsoft/OCR-Form-Tools/commit/3044cc51a9166877bb4f01f28753171b82c04ccd))
+* feat: add current list item style ([#601](https://github.com/microsoft/OCR-Form-Tools/commit/3e503e75513e44e6a90bd013d8dd15c3096cd7e9))
+* fix: remove project from app if security token does not exist ([#468](https://github.com/microsoft/OCR-Form-Tools/commit/730e1963a06f038a4efa9750fcef4be6f15a8460))
+
+### 2.1-preview.1-d859d38 (09-27-2020)
+* fix ,update document state when preview (#317) ([#471](https://github.com/microsoft/OCR-Form-Tools/commit/d859d38ecc1f96b194ffa130a1840f5a7d9b1a9b))
+* refactor: change the confidence value format to percentage ([#461](https://github.com/microsoft/OCR-Form-Tools/commit/e806b4e0dfcc68e6408e2130a46a318637a482a8))
+
+### 2.1-preview.1-7a3f7a7 (09-25-2020)
+* security: upgrade node-forge ([#622](https://github.com/microsoft/OCR-Form-Tools/commit/7a3f7a773c8b01f443afaad89d7974a5bbb0b869))
+* fix: disable move tag and support renaming when searching ([#618](https://github.com/microsoft/OCR-Form-Tools/commit/cac1e8e6cfb2805a6540f9e80d564a0ff8be81c7))
+
+### 2.1-preview.1-4163edc (09-23-2020)
+* docs: add latest tag reference to changelog ([#608](https://github.com/microsoft/OCR-Form-Tools/commit/4163edc18bc65234e263703fc829d2f297953385))
+* fix: use region instead of drawnRegion for labelType in label file ([#582](https://github.com/microsoft/OCR-Form-Tools/commit/ffafc200249a1c47698fedb279b4b55cef0190ba))
+* docs: update readme with docker hub info ([#604](https://github.com/microsoft/OCR-Form-Tools/commit/63bbea076d598d0286095fa0eca48d8c9d0ed706))
+* fix: remove opening browser for yarn start ([#605](https://github.com/microsoft/OCR-Form-Tools/commit/f6c4dc3585df71d09252a28f65e835a594389118))
+* fix: update changelog updater script ([#607](https://github.com/microsoft/OCR-Form-Tools/commit/7c4848c3a72259562c0461f0e2eadfb4a660fa64))
+
+### 2.1-preview.1-f2db74e (09-17-2020)
+* docs: udpate changlog with docker image reference ([#590](https://github.com/microsoft/OCR-Form-Tools/commit/f2db74e322c32338eba3b2df06c01a51cfb7ebc1))
+
+### 2.1-preview.1-1a6b78e (09-16-2020)
+* fix: normalize folder path starting with a period ([#592](https://github.com/microsoft/OCR-Form-Tools/commit/1a6b78e054235da3188aafbe65636a8c18b439bf))
+* fix: change label folder uri title ([#588](https://github.com/microsoft/OCR-Form-Tools/commit/7e4233e568d94817e23dda5ef5513b9ee7475d11))
+
+### 2.1-preview.1-6a1ced5 (09-15-2020)
+* fix: initialize drag pan for analyze page ([#586](https://github.com/microsoft/OCR-Form-Tools/commit/6a1ced5a0bfb03ceba515faddbfa010ac8451460))
+* fix: zoomIn keyboar shortcut for macOS ([#581](https://github.com/microsoft/OCR-Form-Tools/commit/5afeebfee28e10e390f073990f90348c5117475f))
+* fix: appId ([#584](https://github.com/microsoft/OCR-Form-Tools/commit/e053b151441e956641ed05c29106d02358a40792))
+* fix: remove escape quote from release script ([#579](https://github.com/microsoft/OCR-Form-Tools/commit/bd5d51e8e15809b95f15bc495f7d0f91fecfc22d))
+* Stew ro/support drag pan for release ([#576](https://github.com/microsoft/OCR-Form-Tools/commit/77620eccd21d564473c81b43341f59de22339248))
+
+### 2.1-preview.1-0633507 (09-14-2020)
+* Update README.md([#??](https://github.com/microsoft/OCR-Form-Tools/commit/0633507aa767f996add313ced06c2365c5f240c8))
+
+### 2.1-preview.1-8d2286f (09-13-2020)
+* persist trainPage inputs in localStorage ([#568](https://github.com/microsoft/OCR-Form-Tools/commit/8d2286f50236e41fe5540dbb9b161ea88bbf2d7a))
+
+### 2.1-preview.1-bb23e31 (09-11-2020)
+* build(deps): bump node-fetch from 2.6.0 to 2.6.1 ([#575](https://github.com/microsoft/OCR-Form-Tools/commit/bb23e3199c5721338241c8c5ccc0bda104fd15f8))
+* fix: support multiple env files ([#574](https://github.com/microsoft/OCR-Form-Tools/commit/cf64a8ddde05e7e73cad37d271f5d6dfa61c5d7f))
+* fix: "Azure blob storage"  error on on premise scenario ([#572](https://github.com/microsoft/OCR-Form-Tools/commit/46f0bc59f3a531c366bc2c7cec955d2cb6ed7cd6))
+* fix ([#563](https://github.com/microsoft/OCR-Form-Tools/commit/28c792e10692e3cc1f511852ffc9fdbc8dcdda8a))
+
+### 2.1-preview.1-7e828ff (09-10-2020)
+* fix: allow training with placeholder ([#569](https://github.com/microsoft/OCR-Form-Tools/commit/7e828ff02ff8a22b64b4b7d16787d77afb76af62))
+* docs: update changelog ([#564](https://github.com/microsoft/OCR-Form-Tools/commit/1dec72c5df3206554a1e0864b65cc769835785fd))
+* Yongbing chen/human in the loop ([#517](https://github.com/microsoft/OCR-Form-Tools/commit/be9d56481510e3033bcd705743c1ee9aeee20522))
+* fix: support project folder in project settings for local file system ([#559](https://github.com/microsoft/OCR-Form-Tools/commit/b92b73bb8076f9b9bb55dd38fcd223b7b93eaa2e))
+* feat: enable canvas rotation ([#553](https://github.com/microsoft/OCR-Form-Tools/commit/c27a110251df1fc7a595524846e20fd09c79f915))
+* fix: handle tag is undefined error ([#557](https://github.com/microsoft/OCR-Form-Tools/commit/7e4d3fbbc3a2bf925c126cd2b3f493cca48e7a62))
+
+### 2.1-preview.1-193520e (09-08-2020)
+* fix: accept selection of only .fott files for open local project ([#554](https://github.com/microsoft/OCR-Form-Tools/commit/193520e2c3e40b58c3612507efc2249aaf4e9d05))
+* fix: use default shared folder for label URI when training ([#551](https://github.com/microsoft/OCR-Form-Tools/commit/656de2ff07c2083affc2adf52f1a56c5a9c024b8))
+* fix: show label folder uri while training ([#539](https://github.com/microsoft/OCR-Form-Tools/commit/0ad389c06328cd6428653bb7d94d5af716e02ab7))
+* feat: add canvas command bar to analyze page with only zoom buttons ([#549](https://github.com/microsoft/OCR-Form-Tools/commit/895b52740cd1e8d61b5b021e6c0d992f44ce8052))
+
+ ### 2.1-preview.1-4852c84 (09-05-2020)
+* fix buttons styles - makes them more visible ([#526](https://github.com/microsoft/OCR-Form-Tools/commit/4852c8429d25b5569c3335b014da5972cbcc6162))
+
+### 2.1-preview.1-343ea16 (09-04-2020)
+* refactor: remove array for drawn region labels ([#542](https://github.com/microsoft/OCR-Form-Tools/commit/343ea16e18199ab5098395ae8b7a164cd8bab55e))
+* fix: add key prop to region icon ([#540](https://github.com/microsoft/OCR-Form-Tools/commit/87b69093f2d35d91a2a939c46ac66ba4d22a5cb7))
+
+### 2.1-preview.1-b370c9a (09-02-2020)
+* fix: resize canvas on asset preview resize ([#535](https://github.com/microsoft/OCR-Form-Tools/commit/b370c9a9bcf7da416944c626f6d4fd7bd29088bb))
+
+### 2.1-preview.1-de1c304 (08-31-2020)
+* refactor: upgrade tsconfig es2017 to esnext ([#531](https://github.com/microsoft/OCR-Form-Tools/commit/de1c30410b860c9576108f076ec4dd8273e61a79))
+
+### 2.1-preview.1-530545c (08-28-2020)
+* fix: remove existing bounding boxes from document on analyze ([#523](https://github.com/microsoft/OCR-Form-Tools/commit/6a1aedfb89b0499a0f4782e16ccbd8a06887841d))
+* feat: enable download JSON of trained model ([#513](https://github.com/microsoft/OCR-Form-Tools/commits/master))
+
+### 2.1-preview.1-529a0e8 (08-27-2020)
+* fix: show loading indicator while loading model info ([#514](https://github.com/microsoft/OCR-Form-Tools/commit/529a0e819f4cb405e290f34d18d15c487a7bcfad))
+* docs: update telemetry disclaimer ([#521](https://github.com/microsoft/OCR-Form-Tools/pull/521))
+* fix: disable clearing of drawn regions on analyze page ([#518](https://github.com/microsoft/OCR-Form-Tools/commit/298d7c97da1278996d2ee6020d3face0785bc4eb))
+
+### 2.1-preview.1-b2d9a0b (08-26-2020)
+* docs: notice that telemetry is disabled ([#501](https://github.com/microsoft/OCR-Form-Tools/commit/b2d9a0b008ebf350dfcb5fe897fc5dfe0d4d5cb6))
+
+### 2.1-preview.1-d9db4ee (08-24-2020)
+* refactor: upgrade storage-blob to v12.1.2 ([#509](https://github.com/microsoft/OCR-Form-Tools/commit/d9db4ee027240a82feef5b54e5e406c3793d8050))
+* feat: support region labeling ([#481](https://github.com/microsoft/OCR-Form-Tools/commit/dd78ed06761a341908bdb1b09e73fd1f2868431c))
+* feat: support adding model to recent models from compose page ([#510](https://github.com/microsoft/OCR-Form-Tools/commit/65fc92b5737ceea14ff89aa78052be26835ad0ae))
+
+### 2.1-preview.1-2402cba (08-17-2020)
+* fix: notify error message when open project with invalid security token ([#506](https://github.com/microsoft/OCR-Form-Tools/commit/2402cbaf73eba47ad188f851227c04cd44a208d4))
+
+### 2.1-preview.1-a8ef8fa (08-17-2020)
+* fix: don't allow create or update connection with duplicate name ([#486](https://github.com/microsoft/OCR-Form-Tools/commit/a8ef8fab603b3d2c08c533cb5dfe67da117942a0))
+
+### 2.1-preview.1-530545c (08-14-2020)
+* fix: "failed to fetch()" error ([#491](https://github.com/microsoft/OCR-Form-Tools/commit/530545c7cd2b4a3ff444e9c7e1f40c68d4a7376c))
+* fix: sync layer visibility ([#497](https://github.com/microsoft/OCR-Form-Tools/commit/bea552b28acb9b652ffaedf40009d6df5a3197ef))
+* refactor: disable telemetry service ([#498](https://github.com/microsoft/OCR-Form-Tools/commit/6e3628cf174f954693380aab6ebd2dabe027ac6d))
+* fix: change share class name for adblocker chrome extension ([#492](https://github.com/microsoft/OCR-Form-Tools/commit/aa8a73afc6344f3164e79f236d5fa4bb0f64d364))
+
+### 2.1-preview.1-da405b3 (08-10-2020)
+* fix: restrict tag type through hot keys ([#482](https://github.com/microsoft/OCR-Form-Tools/commit/da405b354428b829e895a35a020736b1d88c153f))
+* docs: add share project description to README ([#488](https://github.com/microsoft/OCR-Form-Tools/commit/7ee215f735a84aaa30201748d19207bcc6a05580))
+
+### 2.1-preview.1-29d1f93 (08-07-2020)
+* fix: handle multi selection of non-compatible types with multi-selection tool ([#487](https://github.com/microsoft/OCR-Form-Tools/commit/29d1f93a290e55fdd84f8cf2ee9a914fed702beb))
+
+### 2.1-preview.1-cef225f (08-06-2020)
+* fix: handle undefined image map error ([#462](https://github.com/microsoft/OCR-Form-Tools/commit/cc9e9bfc8fe00bb0ed154edb791446f28060af4e))
+* fix: handle undefined image map error ([#479](https://github.com/microsoft/OCR-Form-Tools/commit/cef225f3346628e79c46e799303400965f1d3c96))
+
+### 2.1-preview.1-76945df (08-05-2020)
+* fix: use english for telemetry reporting ([#472](https://github.com/microsoft/OCR-Form-Tools/commit/76945df3bdf9caba3ba13f4541e17e75b9574b33))
+* fix: resolve unhandled exeptions and new message for OCR service on 400 ([#470](https://github.com/microsoft/OCR-Form-Tools/commit/76381bc659a365ead19387b933485530d2d5edc3))
+* feature: enable popup with composed model info ([#460](https://github.com/microsoft/OCR-Form-Tools/commit/c1f5d803f047e5ca0d18fea6383b3baf56d116ff))
+
+### 2.1-preview.1-f4d53ce (08-03-2020)
+* fix: bump elliptic from 6.5.2 to 6.5.3 ([#469](https://github.com/microsoft/OCR-Form-Tools/commit/f4d53cec967194445885bd3748096f0a3ce10715))
+* feat: add modelCompose icon and created time ([#466](https://github.com/microsoft/OCR-Form-Tools/commit/2fa32ef5f77ec7bb44bf42e9fc0a5fdf7f0330c3))
+
+### 2.1-preview.1-78996ea (07-31-2020)
+* refactor: relocate share button ([#464](https://github.com/microsoft/OCR-Form-Tools/commit/78996ea65616b28d7471b59f4f16f254d7d33127))
+
+### 2.1-preview.1-0e1b637 (07-29-2020)
+* feat: show only ready models in the list ([#459](https://github.com/microsoft/OCR-Form-Tools/commit/0e1b637003f289c56955342f44963003c1543436))
+
+### 2.1-preview.1-84f8285 (07-27-2020)
+* fix: show message on model composition fail ([#457](https://github.com/microsoft/OCR-Form-Tools/commit/84f82859122ff298bcfcca78e821e8bfe437bb78))
+* refactor: add background on popup table ([#446](https://github.com/microsoft/OCR-Form-Tools/commit/27f60df5617da2efba8ffdd601233e0c0f4c8e3e))
+
+### 2.1-preview.1-79264e3 (07-24-2020)
+* fix: handle rejection for security token not found when opening projects ([#441](https://github.com/microsoft/OCR-Form-Tools/commit/79264e3fddfb2c80b88bf8ca21df1e869082ffcf))
+* fix: show more refined error message for model not found analysis error ([#454](https://github.com/microsoft/OCR-Form-Tools/commit/1cb4133dca0092559e7524dfad8c0bf54502dc81))
+* feat: support group selection of words with drawn bounding box ([#447](https://github.com/microsoft/OCR-Form-Tools/commit/b4332a926b1925024a33731a90d303c0b171935b))
+* feat: add apiVersion to telemetry ([#448](https://github.com/microsoft/OCR-Form-Tools/commit/55be5427e4a2f9c8cf393d446049527c55f841d4))
+* fix: margin for filenames in asset preview ([#451](https://github.com/microsoft/OCR-Form-Tools/commit/fe8258f9c7ceba663a66708b19bc0e6556e777ad))
+* docs: add telemetry disclaimer to readme ([#449](https://github.com/microsoft/OCR-Form-Tools/commit/87356a1cf6678bb9494e83178bf6282ca366921f))
+
+### 2.1-preview.1-9b5b99d (07-23-2020)
+* docs: add get-sas.png (https://github.com/microsoft/OCR-Form-Tools/commit/9b5b99d5468661481ae8165593d5a74471366429)
+* doc: add a screenshot of getting SAS token (https://github.com/microsoft/OCR-Form-Tools/commit/87b1062125ed106ff73c036e33f1bf7a5f2c3def)
+* fix: handle undefined error for pdf asset preview memory cleaning ([#442](https://github.com/microsoft/OCR-Form-Tools/commit/9b5b99d5468661481ae8165593d5a74471366429))
+* fix: remove duplicate models in model composed model list ([#439](https://github.com/microsoft/OCR-Form-Tools/commit/7fcc9ccfdb6634326ddd6cbfe99b423300b94131))
+* feat: enable internal telemetry ([#431](https://github.com/microsoft/OCR-Form-Tools/commit/41294c8aa19c82643fe0df669c21a0112668e0dd))
+
+### 2.1-preview.1-f4b4d5d (07-21-2020)
+* fix: use table for model selection info ([#438](https://github.com/microsoft/OCR-Form-Tools/commit/f4b4d5ded4b7e0ff2116ba3b8f97e49fbf30b7c0))
+* fix: reset model name after training ([#434](https://github.com/microsoft/OCR-Form-Tools/commit/ed919a016b150d0938aee25b5550bacf29f04e83))
+* fix: wait for loadeding project with sharing project ([#435](https://github.com/microsoft/OCR-Form-Tools/commit/fc4cb96d2a9d0920c3bbbd9c2000fb4b1b7ac9c0))
+
+### 2.1-preview.1-46dbb2b (07-20-2020)
+* fix: handle no recent models for model compose ([#432](https://github.com/microsoft/OCR-Form-Tools/commit/46dbb2be9ee6100a8f3e6a443ad5e734c60954bb))
+* refactor: use new model compose icon ([#425](https://github.com/microsoft/OCR-Form-Tools/commit/932fb3fd7f84636e97035f4cafadc87cff18b3b3))
+* fix: support long model names for model selection ([#427](https://github.com/microsoft/OCR-Form-Tools/commit/a0fa2daf4cd3286f7f58dc2919fd202115e8d5be))
+* feat add recent models to top of model compose page's list ([#430](https://github.com/microsoft/OCR-Form-Tools/commit/cf8de6be61b95bfe8c937946df71ea81aecb35f9))
+* fix: check valid connection ([#428](https://github.com/microsoft/OCR-Form-Tools/commit/9cb6c5830afddc9317ffdfe6927b581c4d39ba39))
+
+### 2.1-preview.1-162a766 (07-17-2020)
+* refactor: make confidence results same as JSON results ([#409](https://github.com/microsoft/OCR-Form-Tools/commit/162a7660cfe32b72c4954a147269c5d2b7f55a08))
+* fix: prevent user from leaving page while composing ([#422](https://github.com/microsoft/OCR-Form-Tools/commit/63e179d0152d2f8f2ee764443785efa24e5f7dce))
+* feat: support model selection ([#419](https://github.com/microsoft/OCR-Form-Tools/commit/b4c4cc5a8a980aaa6530e7a4a5a1c43e77494c75))
+* feat: share project ([#344](https://github.com/microsoft/OCR-Form-Tools/commit/d059580cfefa053670c45c5d8ec7bf250bc4db27))
+
+### 2.1-preview.1-89be3ac (07-15-2020)
+* fix: on assetFormat undefined ([#413](https://github.com/microsoft/OCR-Form-Tools/commit/89be3ac5b614e91607d7fb8065ad32b69886040d))
+* fix: make sure token names are unique ([#404](https://github.com/microsoft/OCR-Form-Tools/commit/d8fa6141cff4d00ba22e95ef4f5dcc9102e1c1c2))
+* fix: model info enclosing element error on [#407](https://github.com/microsoft/OCR-Form-Tools/issues/407) ([#408](https://github.com/microsoft/OCR-Form-Tools/commit/8cc421c3fee0e781211efb0aeb2b345075012daa))
+* fix: display composed icon for composed model with attribute ([#399](https://github.com/microsoft/OCR-Form-Tools/commit/18fb4d71052b9355c8d5a4f7dde956ba17ca30fa))
+
+### 2.1-preview.1-b67191c (07-09-2020)
+* fix: don't allow choosing not-ready models for compose ([#394](https://github.com/microsoft/OCR-Form-Tools/commit/b67191cdbc872b9004be30aa4b4dfde9a88dfe37))
+* feat: track five most recent project models ([#395](https://github.com/microsoft/OCR-Form-Tools/commit/05850603d51a6786c8b6e8b4a553db020df56158))
+
+### 2.1-preview.1-abc6376 (07-08-2020)
+* feat: enable model info in analyze results ([#383](https://github.com/microsoft/OCR-Form-Tools/commit/abc63767e97dd28a6bb9028e03f2225e6ac0f1ab))
+* fix: check invalid provider options before project actions ([#390](https://github.com/microsoft/OCR-Form-Tools/commit/212647d4327d9e18e9248a2d39086eeaab404979))
+
+### 2.1-preview.1-a334cfc (07-07-2020)
+* fix: hide extra scrollbars for model compose view ([#380](https://github.com/microsoft/OCR-Form-Tools/commit/a334cfc45fc5ab137682ad2b48dd0ec1585055dc))
+* fix: handle version change state mutation error ([#382](https://github.com/microsoft/OCR-Form-Tools/commit/8991cc0c92f2f5cbd226f7e1c5c0825b7af8937c))
+* fix: handle pdf worker terminated error ([#381](https://github.com/microsoft/OCR-Form-Tools/commit/adc0498c31bfd5ba57ab98c373e73575589ab1e1))
+
+### 2.1-preview.1-7192170 (07-02-2020)
+* feat: support release ([#361](https://github.com/microsoft/OCR-Form-Tools/commit/7192170d73d24a43e7fff18cd2c6bae7f208f1b0))
+
+### 2.1-preview.1-978dabc (07-01-2020)
+* feat: support document management ([#374](https://github.com/microsoft/OCR-Form-Tools/commit/978dabc3ba877ed4215865cba2a583fb785a2894))
+
+### 2.1-preview.1-56a4b89 (06-30-2020)
+* fix: wait until composed model is ready ([#369](https://github.com/microsoft/OCR-Form-Tools/commit/56a4b89f370f2fd72c6bc275376205e7fffe6a9e))
+
+### 2.1-preview.1-6114d64 (06-23-2020)
+* fix: update OCR version ([#335](https://github.com/microsoft/OCR-Form-Tools/commit/6114d6456b27a59335e534eef72cefd1b2f15737))
+* feat: support electron for on premise solution ([#333](https://github.com/microsoft/OCR-Form-Tools/commit/ca0bd0c2ab46b7b587e5bfbc60c29b62bb325297))
+
+### 2.1-preview.1-8297b18 (06-19-2020)
+* refactor: put api version in constants ([#332](https://github.com/microsoft/OCR-Form-Tools/commit/8297b18a084be86bc4c986a1a332cb40bd807d1b))
+
+### 2.1-preview.1-3b7f803 (06-18-2020)
+* feat: enable model compose (preview) ([#328](https://github.com/microsoft/OCR-Form-Tools/commit/3b7f803407b82191706120bb9f12b82de1955704))
+* fix: quick reordering tags ([#322](https://github.com/microsoft/OCR-Form-Tools/commit/3cc5267ef8617590adb3d4966f75cfed64604f00))
+* feat: localization for canvas commandbar items ([#319](https://github.com/microsoft/OCR-Form-Tools/commit/253b9c90eb4923e7fde015a7216905fa32a8dcfa))
+* feat: enable re-run OCR ([#297](https://github.com/microsoft/OCR-Form-Tools/commit/cbe9b0ed1c48f54c100b31b7f04706a969df2dd5))
+* fix: capitalize python in analyze page ([#320](https://github.com/microsoft/OCR-Form-Tools/commit/96626636a96a3d19030df283ac794fa9c2aab18c))
+* fix: fix spelling correction for string match ([#318](https://github.com/microsoft/OCR-Form-Tools/commit/28e53cefcf0bb462d547d6e38b24c480c03b946f))
+* feature: keep prediction in UI ([#285](https://github.com/microsoft/OCR-Form-Tools/commit/dad98b9bd1d305a6bfeb2846ef4067da186ff801))
+
+### 2.0.0-1c39800 (06-05-2020)
+* feat: add description - how to delete info ([#292](https://github.com/microsoft/OCR-Form-Tools/commit/1c39800b1152f186dfc19834bb969abbc4fe0ac2))
+* feat: enable download analyze script ([#304](https://github.com/microsoft/OCR-Form-Tools/commit/9c97ed0ff9b0aa72ec9a197fc92f3a5998135c36))
+* fix: check ocrread results before getting image extent ([#296](https://github.com/microsoft/OCR-Form-Tools/commit/61dba02fc6f19eb854e1f499e475b1336e6171b9))
+* feat: Add better error message for CORS ([#289](https://github.com/microsoft/OCR-Form-Tools/commit/8f210792b4d84e424b00499efb540b0e27e9fdad))
+
+### 2.0.0-2760166 (05-30-2020)
+* fix: fix mime check bug for jpeg/jpg and tiff ([#291](https://github.com/microsoft/OCR-Form-Tools/commit/2760166bcb809bbfdc207b01db49f00153318624))
+* refactor: simplify shortcut descriptions ([#277](https://github.com/microsoft/OCR-Form-Tools/commit/db95b0e2510f6cef9bc7279fe0a19dce239c816e))
+
+### 2.0.0-a5e4e07 (05-21-2020)
+* feature: show table view when table icon is clicked ([#271](https://github.com/microsoft/OCR-Form-Tools/commit/a5e4e079d4c0d1c7c52e3b015c0ddf9b8601bbf2))
+
+### 2.0.0-814276a (05-20-2020)
+* fix: modify skip button according to feedback comments ([#264](https://github.com/microsoft/OCR-Form-Tools/commit/814276af6f4259844854798adf0c56bd606b2363))
+* feature: keyboard shortcuts and tips ([#258](https://github.com/microsoft/OCR-Form-Tools/commit/37aa859a80dc0213a118313558ad21ba424008e7))
+* feat: add electron mode from VoTT project ([#260](https://github.com/microsoft/OCR-Form-Tools/commit/2a3383d4a0f100a39ed40627bdffb9b48f78f5df))
+* refactor: use forEach instead of map in handleFeatureSelect ([#259](https://github.com/microsoft/OCR-Form-Tools/commit/c1c590c463743d187fda2429a628e27c6c42012f))
+
+### 2.0.0-0061645 (05-13-2020)
+* build: update nginx base image version to 1.18.0-alpine ([#255](https://github.com/microsoft/OCR-Form-Tools/commit/0061645871806595e4fe2ab5991cc494afa26b31))
+* fix: assign empty string when predict item's fieldName is undefined ([#254](https://github.com/microsoft/OCR-Form-Tools/commit/d4d919f678b1f162f48c87ee5223281e57945a0a))
+* fix: overlaping left split pane ([#252](https://github.com/microsoft/OCR-Form-Tools/commit/2e8c351f74c385b8627ee6ea39f974e5e048ea8d))
+* refactor: change predict to analyze in UI while keeping predict term ([#147](https://github.com/microsoft/OCR-Form-Tools/commit/c9aa58e36a10a35083249a8080c2cfb9fccf3733))
+### 2.0.0-7c7ba93 (05-07-2020)
+* fix: check null value from post processed value ([#248](https://github.com/microsoft/OCR-Form-Tools/commit/a361189c527bfffd6417f90a2521ad40b2b3f205))
+* feat: enable outputting to file for analyze script ([#246](https://github.com/microsoft/OCR-Form-Tools/commit/7c7ba937f140490775b788d63ef2c7ed63ca40f1))
+### 2.0.0-9d91800 (05-06-2020)
+* fix: prevent user from changing tag types when invalid ([#224](https://github.com/microsoft/OCR-Form-Tools/commit/d8823a33591db5c5dc9a0af753e007167218a3e3))
+* fix: prevent user from adding multiple checkboxes to a single tag ([#224](https://github.com/microsoft/OCR-Form-Tools/commit/d8823a33591db5c5dc9a0af753e007167218a3e3))
+* fix: display error when inputted SAS doesn't contain token ([#243](https://github.com/microsoft/OCR-Form-Tools/commit/9826ca8504549f23057c9cad1baebc5e9d1f6fe7))
+### 2.0.0-25d3298 (05-04-2020)
+* feat: track document count for tags ([#231](https://github.com/microsoft/OCR-Form-Tools/commit/70a6e43dc54239cdc153d5d328b17c1dfa0f085f))
+* fix: display error when inputted service URI contains path or query ([#234](https://github.com/microsoft/OCR-Form-Tools/commit/04a16961b37ad5b5d01fc4c93addaaf69cbf0e72))
+* feat: add link in status bar to CHANGELOG ([#233](https://github.com/microsoft/OCR-Form-Tools/commit/e66646a13263239213580378bbd2d8462d7e22b6))
+### 2.0.0-f6c8ffa (05-01-2020)
+* refactor: change checkbox to selectionMark ([#223](https://github.com/microsoft/OCR-Form-Tools/commit/f6c8ffad6edf23f6241f314e9456da92bc1a8402))
+### 2.0.0-f3e42f6 (04-30-2020)
+* feat: display post-processed value in analyzed results ([#229](https://github.com/microsoft/OCR-Form-Tools/commit/f3e42f6e8e9e934f1a241921dbe4a1e8d311bb46))
+### 2.0.0-f068866 (04-28-2020)
+* fix: hide sprin in tag input control when open an empty folder ([#220](https://github.com/microsoft/OCR-Form-Tools/commit/f0688668df2e676fce9749fad8ec9d39e56697cf))
+* perf: cache images, reduce canvas size, and fix memory leak for asset preview ([#218](https://github.com/microsoft/OCR-Form-Tools/commit/e8ad9a3bebf2a1ae210e0e1fa3eebba564592c4c))
+### 2.0.0-595a512 (04-24-2020)
+* fix: align rotated picture asset with OCR result
+### 2.0.0-51c02cc (04-20-2020)
+* fix: scrollbar fix when page size changes
+* fix: Add split pane to fix too long tag name is invisible in right sidebar
+### 2.0.0-202fb2f (04-20-2020)
+* perf: improve assets loading performance and fix some bugs
+### 2.0.0-bce554e (04-16-2020)
+* perf: improve Azure Blob file list performance
+* feat: support URL upload for predicting file
+### 2.0.0-ef18425 (04-09-2020)
+* feat: enable checkbox labeling (preview)
diff --git a/package.json b/package.json
index 70c05d4f1..c16cc959f 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,9 @@
         "reactstrap": "^8.2.0",
         "redux": "^4.0.4",
         "redux-thunk": "^2.3.0",
+        "rfdc": "^1.1.4",
+        "rimraf": "^3.0.2",
+        "serialize-javascript": "^5.0.1",
         "shortid": "^2.2.15",
         "utif": "^3.1.0",
         "vott-react": "^0.2.12"
diff --git a/src/assets/sass/fabric-icons-inline.scss b/src/assets/sass/fabric-icons-inline.scss
index 21b28afe0..75596903b 100644
--- a/src/assets/sass/fabric-icons-inline.scss
+++ b/src/assets/sass/fabric-icons-inline.scss
@@ -3,7 +3,7 @@
 */
 @font-face {
     font-family: 'FabricMDL2Icons';
-    src: url('data:application/octet-stream;base64,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') format('truetype');
+    src: url('data:application/octet-stream;base64,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') format('truetype');
   }
 
   .ms-Icon {
@@ -19,6 +19,7 @@
   // Mixins
   @mixin ms-Icon--Add { content: "\E710"; }
   @mixin ms-Icon--AddField { content: "\E4C7"; }
+  @mixin ms-Icon--AddTable { content: "\E4C6"; }
   @mixin ms-Icon--AddTo { content: "\ECC8"; }
   @mixin ms-Icon--AlertSolid { content: "\F331"; }
   @mixin ms-Icon--AutoEnhanceOff { content: "\E78E"; }
@@ -46,15 +47,19 @@
   @mixin ms-Icon--Down { content: "\E74B"; }
   @mixin ms-Icon--Download { content: "\E896"; }
   @mixin ms-Icon--Edit { content: "\E70F"; }
+  @mixin ms-Icon--EditTable { content: "\E4C4"; }
   @mixin ms-Icon--FieldChanged { content: "\F2C3"; }
   @mixin ms-Icon--FieldNotChanged { content: "\F2C4"; }
   @mixin ms-Icon--Filter { content: "\E71C"; }
+  @mixin ms-Icon--FixedColumnWidth { content: "\E3EA"; }
   @mixin ms-Icon--GroupedList { content: "\EF74"; }
   @mixin ms-Icon--GroupList { content: "\F168"; }
   @mixin ms-Icon--Help { content: "\E897"; }
   @mixin ms-Icon--Hide3 { content: "\F6AC"; }
   @mixin ms-Icon--Home { content: "\E80F"; }
   @mixin ms-Icon--Info { content: "\E946"; }
+  @mixin ms-Icon--InsertColumnsRight { content: "\F64B"; }
+  @mixin ms-Icon--InsertRowsBelow { content: "\F64D"; }
   @mixin ms-Icon--Insights { content: "\E3AF"; }
   @mixin ms-Icon--KeyPhraseExtraction { content: "\E395"; }
   @mixin ms-Icon--Label { content: "\E932"; }
@@ -83,11 +88,17 @@
   @mixin ms-Icon--StatusCircleCheckmark { content: "\F13E"; }
   @mixin ms-Icon--System { content: "\E770"; }
   @mixin ms-Icon--Table { content: "\ED86"; }
+  @mixin ms-Icon--TableBrandedColumn { content: "\E3F1"; }
+  @mixin ms-Icon--TableBrandedRow { content: "\E3EE"; }
+  @mixin ms-Icon--TableFirstColumn { content: "\E3EF"; }
+  @mixin ms-Icon--TableGroup { content: "\F6D9"; }
+  @mixin ms-Icon--TableHeaderRow { content: "\E3EC"; }
   @mixin ms-Icon--Tag { content: "\E8EC"; }
   @mixin ms-Icon--TagGroup { content: "\E3F6"; }
   @mixin ms-Icon--TextDocument { content: "\F029"; }
   @mixin ms-Icon--TextField { content: "\EDC3"; }
   @mixin ms-Icon--Up { content: "\E74A"; }
+  @mixin ms-Icon--UpdateRestore { content: "\E777"; }
   @mixin ms-Icon--View { content: "\E890"; }
   @mixin ms-Icon--WarningSolid { content: "\F736"; }
   @mixin ms-Icon--ZoomIn { content: "\E8A3"; }
@@ -97,6 +108,7 @@
   // Classes
   .ms-Icon--Add:before { @include ms-Icon--Add }
   .ms-Icon--AddField:before { @include ms-Icon--AddField }
+  .ms-Icon--AddTable:before { @include ms-Icon--AddTable }
   .ms-Icon--AddTo:before { @include ms-Icon--AddTo }
   .ms-Icon--AlertSolid:before { @include ms-Icon--AlertSolid }
   .ms-Icon--AutoEnhanceOff:before { @include ms-Icon--AutoEnhanceOff }
@@ -124,15 +136,19 @@
   .ms-Icon--Down:before { @include ms-Icon--Down }
   .ms-Icon--Download:before { @include ms-Icon--Download }
   .ms-Icon--Edit:before { @include ms-Icon--Edit }
+  .ms-Icon--EditTable:before { @include ms-Icon--EditTable }
   .ms-Icon--FieldChanged:before { @include ms-Icon--FieldChanged }
   .ms-Icon--FieldNotChanged:before { @include ms-Icon--FieldNotChanged }
   .ms-Icon--Filter:before { @include ms-Icon--Filter }
+  .ms-Icon--FixedColumnWidth:before { @include ms-Icon--FixedColumnWidth }
   .ms-Icon--GroupedList:before { @include ms-Icon--GroupedList }
   .ms-Icon--GroupList:before { @include ms-Icon--GroupList }
   .ms-Icon--Help:before { @include ms-Icon--Help }
   .ms-Icon--Hide3:before { @include ms-Icon--Hide3 }
   .ms-Icon--Home:before { @include ms-Icon--Home }
   .ms-Icon--Info:before { @include ms-Icon--Info }
+  .ms-Icon--InsertColumnsRight:before { @include ms-Icon--InsertColumnsRight }
+  .ms-Icon--InsertRowsBelow:before { @include ms-Icon--InsertRowsBelow }
   .ms-Icon--Insights:before { @include ms-Icon--Insights }
   .ms-Icon--KeyPhraseExtraction:before { @include ms-Icon--KeyPhraseExtraction }
   .ms-Icon--Label:before { @include ms-Icon--Label }
@@ -161,11 +177,17 @@
   .ms-Icon--StatusCircleCheckmark:before { @include ms-Icon--StatusCircleCheckmark }
   .ms-Icon--System:before { @include ms-Icon--System }
   .ms-Icon--Table:before { @include ms-Icon--Table }
+  .ms-Icon--TableBrandedColumn:before { @include ms-Icon--TableBrandedColumn }
+  .ms-Icon--TableBrandedRow:before { @include ms-Icon--TableBrandedRow }
+  .ms-Icon--TableFirstColumn:before { @include ms-Icon--TableFirstColumn }
+  .ms-Icon--TableGroup:before { @include ms-Icon--TableGroup }
+  .ms-Icon--TableHeaderRow:before { @include ms-Icon--TableHeaderRow }
   .ms-Icon--Tag:before { @include ms-Icon--Tag }
   .ms-Icon--TagGroup:before { @include ms-Icon--TagGroup }
   .ms-Icon--TextDocument:before { @include ms-Icon--TextDocument }
   .ms-Icon--TextField:before { @include ms-Icon--TextField }
   .ms-Icon--Up:before { @include ms-Icon--Up }
+  .ms-Icon--UpdateRestore:before { @include ms-Icon--UpdateRestore }
   .ms-Icon--View:before { @include ms-Icon--View }
   .ms-Icon--WarningSolid:before { @include ms-Icon--WarningSolid }
   .ms-Icon--ZoomIn:before { @include ms-Icon--ZoomIn }
diff --git a/src/common/constants.ts b/src/common/constants.ts
index 68a0ee551..0377228e0 100644
--- a/src/common/constants.ts
+++ b/src/common/constants.ts
@@ -42,6 +42,8 @@ export const constants = {
     autoLabelBatchSizeMax: 10,
     autoLabelBatchSizeMin: 3,
     showOriginLabelsByDefault: true,
+    fieldsSchema: "http://www.azure.com/schema/formrecognizer/fields.json",
+    labelsSchema: "http://www.azure.com/schema/formrecognizer/labels.json",
 
     pdfjsWorkerSrc(version: string) {
         return `https://fotts.azureedge.net/npm/pdfjs-dist/${version}/pdf.worker.js`;
diff --git a/src/common/localization/en-us.ts b/src/common/localization/en-us.ts
index 0610098ca..02662fc88 100644
--- a/src/common/localization/en-us.ts
+++ b/src/common/localization/en-us.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import {IAppStrings} from "../strings";
+import { IAppStrings } from "../strings";
 
 /*eslint-disable no-template-curly-in-string, no-multi-str*/
 
@@ -271,7 +271,7 @@ export const english: IAppStrings = {
         localFile: "Local file",
         url: "URL",
     },
-    layoutPredict:{
+    layoutPredict: {
         layout: "Layout",
         title: "Layout analyze",
         inProgress: "Analysis in progress...",
@@ -337,7 +337,43 @@ export const english: IAppStrings = {
             autoLabel: "Auto-labeled: ",
             revised: "Revised: ",
         },
+        regionTableTags: {
+            configureTag: {
+                errors: {
+                    atLeastOneColumn: "Please assign at least one column.",
+                    atLeastOneRow: "Please assign at least one row.",
+                    checkFields: "Please check if you filled out all required fields correctly.",
+                    assignTagName: "Tag name cannot be empty",
+                    notUniqueTagName: "Tag name should be unique",
+                    emptyTagName: "Please assign name for your table tag.",
+                    emptyName: "Name cannot be empty",
+                    notUniqueName: "Name should be unique",
+                    notCompatibleTableColOrRowType: "\${kind}\ type is not compatible with this type. If you want to change type of this \${kind}\, please remove or assign all labels which using this \${kind}\ in your project.",
+                }
+            },
+            tableLabeling: {
+                title: "Label table",
+                tableName: "Table name",
+                description: {
+                    title: "To start labeling your table:",
+                    stepOne: "Select the words on the document you want to label",
+                    stepTwo: "Click the table cell you want to label selected words to",
+                },
+                buttons: {
+                    done: "Done",
+                    reconfigureTable: "Reconfigure table",
+                    addRow: "Add row"
+                },
+            },
+            confirm: {
+                reconfigure: {
+                    title: "Reconfigure tag",
+                    message: "Are you sure you want to reconfigure this tag? \n It will be reconfigured for all documents.",
+                }
+            }
+        },
         toolbar: {
+            addTable: "Add new table tag",
             add: "Add new tag",
             onlyShowCurrentPageTags: "Only show tags used in current page",
             showAllTags: "Show all tags",
@@ -524,7 +560,7 @@ export const english: IAppStrings = {
                             runOcrOnAllDocuments: "Run Layout on all documents",
                             runAutoLabelingCurrentDocument: "Auto-label the current document",
                             runAutoLabelingOnMultipleUnlabeledDocuments: "Auto-label multiple unlabeled documents",
-                            noPredictModelOnProject: "Predict model not avaliable, please train the model first.",
+                            noPredictModelOnProject: "Predict model not available, please train the model first.",
                         }
                     }
                 },
@@ -620,8 +656,8 @@ export const english: IAppStrings = {
                 description: "Select all labels for a tag on document and press 'delete' key"
             },
             groupSelect: {
-                name: "Select multiple words by drawing a bounding box around encompased words",
-                description: "Press and hold the shift key. Then, click and hold left mouse button. Then, drag the pointer to draw the bounding box around encompased words"
+                name: "Select multiple words by drawing a bounding box around encompassed words",
+                description: "Press and hold the shift key. Then, click and hold left mouse button. Then, drag the pointer to draw the bounding box around encompassed words"
             }
         },
         headers: {
@@ -642,7 +678,7 @@ export const english: IAppStrings = {
         },
         genericRenderError: {
             title: "Error Loading Application",
-            message: "An error occured while rendering the application. Please try again",
+            message: "An error occurred while rendering the application. Please try again",
         },
         projectInvalidSecurityToken: {
             title: "Error loading project file",
@@ -777,7 +813,7 @@ export const english: IAppStrings = {
             tokenNameExist: "Warning! You already have token with same name as in shared project. Please create a new token, and update the existing project which uses ''${sharedTokenName}'' with new token name."
         },
         copy: {
-            success: "Project token copied to clipboard and ready to share. Reciever of project token can click 'Open Cloud Project' from the Home page to use shared token.",
+            success: "Project token copied to clipboard and ready to share. Receiver of project token can click 'Open Cloud Project' from the Home page to use shared token.",
         }
     },
 };
diff --git a/src/common/localization/es-cl.ts b/src/common/localization/es-cl.ts
index 2c2a290cb..728dca275 100644
--- a/src/common/localization/es-cl.ts
+++ b/src/common/localization/es-cl.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import {IAppStrings} from "../strings";
+import { IAppStrings } from "../strings";
 
 /*eslint-disable no-template-curly-in-string, no-multi-str*/
 
@@ -336,7 +336,44 @@ export const spanish: IAppStrings = {
             autoLabel: "Auto-etiquetado: ",
             revised: "Revisado: ",
         },
+        regionTableTags: {
+            configureTag: {
+                errors: {
+                    atLeastOneColumn: "Asigne al menos una columna.",
+                    atLeastOneRow: "Asigne al menos una fila.",
+                    checkFields: "Verifique si completó todos los campos obligatorios correctamente.",
+                    assignTagName: "El nombre de la etiqueta no puede estar vacío.",
+                    notUniqueTagName: "El nombre de la etiqueta debe ser único",
+                    emptyTagName: "Asigne un nombre para la etiqueta de su mesa.",
+                    emptyName: "El nombre no puede estar vacío",
+                    notUniqueName: "El nombre debe ser único",
+                    notCompatibleTableColOrRowType: "El tipo $ {kind} no es compatible con este tipo. Si desea cambiar el tipo de este $ {kind}, elimine o asigne todas las etiquetas que usan este $ {kind} en su proyecto."
+                }
+            },
+            tableLabeling: {
+                title: "Tabla de etiquetas",
+                tableName: "Nombre de la tabla",
+                description: {
+                    title: "Para comenzar a etiquetar su mesa:",
+                    stepOne: "Seleccione las palabras del documento que desea etiquetar",
+                    stepTwo: "Haga clic en la celda de la tabla a la que desea etiquetar las palabras seleccionadas",
+                },
+                buttons: {
+                    done: "Hecho",
+                    reconfigureTable: "Reconfigurar la tabla",
+                    addRow: "Añadir fila"
+                }
+            },
+            confirm: {
+                reconfigure: {
+                    title: "Reconfigurar etiqueta",
+                    message: "¿Está seguro de que desea volver a configurar esta etiqueta?\n Se volverá a configurar para todos los documentos.",
+                }
+            }
+
+        },
         toolbar: {
+            addTable: "Agregar nueva etiqueta",
             add: "Agregar nueva etiqueta",
             onlyShowCurrentPageTags: "Mostrar solo las etiquetas utilizadas en la página actual",
             showAllTags: "Mostrar todas las etiquetas",
diff --git a/src/common/mockFactory.ts b/src/common/mockFactory.ts
index 9062df19f..f80714d43 100644
--- a/src/common/mockFactory.ts
+++ b/src/common/mockFactory.ts
@@ -489,6 +489,7 @@ export default class MockFactory {
             saveAssetMetadataAndCleanEmptyLabel: jest.fn(()=> Promise.resolve()),
             updateProjectTag: jest.fn(() => Promise.resolve()),
             deleteProjectTag: jest.fn(() => Promise.resolve()),
+            reconfigureTableTag: jest.fn(() => Promise.resolve()),
         };
     }
 
diff --git a/src/common/strings.ts b/src/common/strings.ts
index 9a7249de4..052f76ff9 100644
--- a/src/common/strings.ts
+++ b/src/common/strings.ts
@@ -306,22 +306,23 @@ export interface IAppStrings {
             color: string,
         }
         toolbar: {
-            add: string,
-            onlyShowCurrentPageTags: string,
-            showAllTags: string,
+            add: string;
+            addTable: string;
+            contextualMenu: string;
+            delete: string;
+            edit: string;
+            format: string;
+            lock: string;
+            moveDown: string;
+            moveUp: string;
+            rename: string;
+            search: string;
+            type: string;
+            vertiline: string;
+            onlyShowCurrentPageTags:string,
+            showAllTags:string,
             showOriginLabels: string
             hideOriginLabels: string,
-            contextualMenu: string,
-            delete: string,
-            edit: string,
-            format: string,
-            lock: string,
-            moveDown: string,
-            moveUp: string,
-            rename: string,
-            search: string,
-            type: string,
-            vertiline: string,
         }
         colors: {
             white: string,
@@ -346,12 +347,47 @@ export interface IAppStrings {
             notCompatibleTagType: string,
             checkboxPerTagLimit: string,
             notCompatibleWithDrawnRegionTag: string,
-            replaceAllExitingLabels: string,
-            replaceAllExitingLabelsTitle: string,
+            replaceAllExitingLabels:string,
+            replaceAllExitingLabelsTitle:string,
         },
-        preText: {
-            autoLabel: string,
-            revised: string,
+        preText:{
+            autoLabel:string,
+            revised:string,
+        }
+        regionTableTags: {
+            configureTag: {
+                errors: {
+                    atLeastOneColumn: string,
+                    atLeastOneRow: string,
+                    checkFields: string,
+                    assignTagName: string,
+                    notUniqueTagName: string,
+                    emptyTagName: string,
+                    emptyName: string,
+                    notUniqueName: string,
+                    notCompatibleTableColOrRowType: string;
+                },
+            },
+            tableLabeling: {
+                title: string,
+                description: {
+                    title: string,
+                    stepOne: string,
+                    stepTwo: string,
+                },
+                tableName: string,
+                buttons: {
+                    done: string,
+                    reconfigureTable: string,
+                    addRow: string,
+                }
+            },
+            confirm: {
+                reconfigure: {
+                    title: string,
+                    message: string,
+                }
+            }
         }
     };
     connections: {
@@ -677,7 +713,7 @@ interface IErrorMetadata {
     message: string,
 }
 
-interface IStrings extends LocalizedStringsMethods, IAppStrings { }
+interface IStrings extends LocalizedStringsMethods, IAppStrings {}
 
 export const strings: IStrings = new LocalizedStrings({
     en: english,
diff --git a/src/common/themes.ts b/src/common/themes.ts
index 4859da725..2cfbdd530 100644
--- a/src/common/themes.ts
+++ b/src/common/themes.ts
@@ -1,4 +1,4 @@
-import {createTheme, IPalette} from "@fluentui/react";
+import {createTheme, IPalette, DefaultPalette} from "@fluentui/react";
 
 const rightPaneDefaultButtonPalette = {
     themePrimary: "#E9ECEF",
@@ -306,6 +306,7 @@ const subMenuPalette = {
 
 const rightPaneDefaultButtonTheme = createTheme({palette: rightPaneDefaultButtonPalette});
 const defaultDarkTheme = createTheme({palette: DarkDefaultPalette});
+const defaultTheme = createTheme({palette: DefaultPalette});
 const whiteTheme = createTheme({palette: whiteButtonPalette});
 const redTheme = createTheme({palette: redButtonPalette});
 const greenTheme = createTheme({palette: greenButtonPalette});
@@ -356,6 +357,9 @@ export function getGreenWithWhiteBackgroundTheme() {
 export function getDefaultDarkTheme() {
     return defaultDarkTheme;
 }
+export function getDefaultTheme() {
+    return defaultTheme;
+}
 
 export function getSubMenuTheme() {
     return subMenuTheme;
diff --git a/src/common/utils.ts b/src/common/utils.ts
index d4722568c..37f4bb364 100644
--- a/src/common/utils.ts
+++ b/src/common/utils.ts
@@ -2,9 +2,10 @@
 // Licensed under the MIT license.
 
 import Guard from "./guard";
-import { IProject, ISecurityToken, IProviderOptions, ISecureString, ITag } from "../models/applicationState";
+import { IProject, ISecurityToken, IProviderOptions, ISecureString, ITag, FieldType, FieldFormat } from "../models/applicationState";
 import { encryptObject, decryptObject, encrypt, decrypt } from "./crypto";
 import UTIF from "utif";
+import { useState, useEffect } from 'react';
 import {constants} from "./constants";
 import _ from "lodash";
 import JsZip from 'jszip';
@@ -198,7 +199,7 @@ export async function throttle<T>(max: number, arr: T[], worker: (payload: T) =>
 }
 
 export function delay(ms: number) {
-    return new Promise((resolve) => {
+    return new Promise<void>((resolve) => {
         setTimeout(() => {
             resolve();
         }, ms);
@@ -361,6 +362,64 @@ export function fixedEncodeURIComponent(str: string) {
     })
 }
 
+
+/**
+ * Filters tag's format according to chosen tag's type
+ * @param FieldType The json object
+ * @returns [] of corresponding tag's formats
+ */
+export function filterFormat(type: FieldType | string): any[] {
+    switch (type) {
+        case FieldType.String:
+            return [
+                FieldFormat.NotSpecified,
+                FieldFormat.Alphanumeric,
+                FieldFormat.NoWhiteSpaces,
+            ];
+        case FieldType.Number:
+            return [
+                FieldFormat.NotSpecified,
+                FieldFormat.Currency,
+            ];
+        case FieldType.Date:
+            return [
+                FieldFormat.NotSpecified,
+                FieldFormat.DMY,
+                FieldFormat.MDY,
+                FieldFormat.YMD,
+            ];
+        case FieldType.Object:
+        case FieldType.Array:
+            return [
+                FieldFormat.NotSpecified,
+            ];
+        default:
+            return [ FieldFormat.NotSpecified ];
+    }
+}
+
+/**
+ * UseDebounce - custom React hook for handling fast changing values, the hook re-call only if value or delay changes
+ * @param value The value to be changed
+ * @param delay - delay after which the change will be registered in milliseconds
+ */
+export function useDebounce(value: any, delay: number) {
+        const [debouncedValue, setDebouncedValue] = useState(value);
+        useEffect(
+          () => {
+            // Update debounced value after delay
+            const delayHandler = setTimeout(() => {
+              setDebouncedValue(value);
+            }, delay);
+            // cleanup
+            return () => {
+              clearTimeout(delayHandler);
+            };
+          },
+          [value, delay]
+        );
+        return debouncedValue;
+      }
 export function getAPIVersion(projectAPIVersion: string): string {
     return (constants.enableAPIVersionSelection && projectAPIVersion) ? projectAPIVersion : constants.apiVersion;
 }
@@ -416,6 +475,20 @@ export function downloadFile(data: any, fileName: string, prefix?: string): void
     fileLink.click();
 }
 
+export function  getTagCategory (tagType: string) {
+    switch (tagType) {
+        case FieldType.SelectionMark:
+        case "checkbox":
+            return "checkbox";
+            case FieldType.Object:
+                return FieldType.Object;
+            case FieldType.Array:
+                return FieldType.Array;
+        default:
+            return "text";
+    }
+}
+
 export type zipData = {
     fileName: string;
     data: any;
diff --git a/src/config/fabric-icons.json b/src/config/fabric-icons.json
index c29704e75..8f2c19b50 100644
--- a/src/config/fabric-icons.json
+++ b/src/config/fabric-icons.json
@@ -15,12 +15,12 @@
       "unicode": "E4C7"
     },
     {
-      "name": "AddTo",
-      "unicode": "ECC8"
+      "name": "AddTable",
+      "unicode": "E4C6"
     },
     {
-      "name": "AlertSolid",
-      "unicode": "F331"
+      "name": "AddTo",
+      "unicode": "ECC8"
     },
     {
       "name": "AutoEnhanceOff",
@@ -30,6 +30,10 @@
       "name": "AutoEnhanceOn",
       "unicode": "E78D"
     },
+    {
+      "name": "AlertSolid",
+      "unicode": "F331"
+    },
     {
       "name": "AzureAPIManagement",
       "unicode": "F37F"
@@ -122,6 +126,10 @@
       "name": "Edit",
       "unicode": "E70F"
     },
+    {
+      "name": "EditTable",
+      "unicode": "E4C4"
+    },
     {
       "name": "FieldChanged",
       "unicode": "F2C3"
@@ -134,6 +142,10 @@
       "name": "Filter",
       "unicode": "E71C"
     },
+    {
+      "name": "FixedColumnWidth",
+      "unicode": "E3EA"
+    },
     {
       "name": "GroupedList",
       "unicode": "EF74"
@@ -158,6 +170,14 @@
       "name": "Info",
       "unicode": "E946"
     },
+    {
+      "name": "InsertColumnsRight",
+      "unicode": "F64B"
+    },
+    {
+      "name": "InsertRowsBelow",
+      "unicode": "F64D"
+    },
     {
       "name": "Insights",
       "unicode": "E3AF"
@@ -270,6 +290,18 @@
       "name": "Table",
       "unicode": "ED86"
     },
+    {
+      "name": "TableBrandedColumn",
+      "unicode": "E3F1"
+    },
+    {
+      "name": "TableBrandedRow",
+      "unicode": "E3EE"
+    },
+    {
+      "name": "TableGroup",
+      "unicode": "F6D9"
+    },
     {
       "name": "Tag",
       "unicode": "E8EC"
@@ -290,6 +322,10 @@
       "name": "Up",
       "unicode": "E74A"
     },
+    {
+      "name": "UpdateRestore",
+      "unicode": "E777"
+    },
     {
       "name": "View",
       "unicode": "E890"
@@ -307,4 +343,4 @@
       "unicode": "E71F"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/src/models/applicationState.ts b/src/models/applicationState.ts
index feadddfbd..559c17f89 100644
--- a/src/models/applicationState.ts
+++ b/src/models/applicationState.ts
@@ -119,11 +119,28 @@ export interface IFileInfo {
  * @member color - User editable color associated to tag
  */
 export interface ITag {
-    name: string,
-    color: string,
-    type: FieldType,
-    format: FieldFormat,
-    documentCount?: number,
+    name: string;
+    color: string;
+    type: FieldType;
+    format: FieldFormat;
+    documentCount?: number;
+}
+
+export interface ITableTag extends ITag {
+    fields?: ITableField[];
+    itemType?: string;
+    definition?: ITableDefinition,
+    visualizationHint?: TableVisualizationHint,
+}
+
+export enum TableHeaderTypeAndFormat {
+    Rows = "rows",
+    Columns = "columns"
+}
+
+export enum TableVisualizationHint {
+    Horizontal = "horizontal",
+    Vertical = "vertical",
 }
 
 /**
@@ -217,7 +234,14 @@ export interface IRegion {
     boundingBox?: IBoundingBox,
     value?: string,
     pageNumber: number,
+    isTableRegion?: boolean,
     changed?: boolean,
+
+}
+
+export interface ITableRegion extends IRegion {
+    rowKey: string,
+    columnKey: string,
 }
 
 /**
@@ -228,6 +252,8 @@ export interface ILabelData {
     document: string,
     labelingState?: AssetLabelingState;
     labels: ILabel[],
+    tableLabels?: ITableLabel[],
+    $schema?: string,
 }
 
 /**
@@ -244,6 +270,18 @@ export interface ILabel {
     revised?: boolean;
 }
 
+export interface ITableLabel {
+    tableKey: string,
+    labels: ITableCellLabel[],
+}
+
+export interface ITableCellLabel {
+    rowKey: string,
+    columnKey: string,
+    value: IFormRegion[],
+    revised?: boolean;
+}
+
 /**
  * @name - IFormRegion
  * @description - Defines a region which consumed by FormRecognizer
@@ -290,13 +328,39 @@ export interface ISecurityToken {
 }
 
 export interface IField {
-    fieldKey: string,
-    fieldType: FieldType,
-    fieldFormat: FieldFormat,
+    fieldKey: string;
+    fieldType: FieldType;
+    fieldFormat: FieldFormat;
+}
+
+export interface ITableKeyField extends IField {
+    documentCount?: number;
+}
+
+export interface ITableField extends IField {
+    itemType?: string;
+    fields?: ITableField[];
+    visualizationHint?: TableVisualizationHint;
+}
+
+export interface ITableDefinition extends IField {
+    itemType?: string;
+    fields?: ITableField[];
+}
+
+export interface ITableConfigItem {
+    name: string,
+    format: string,
+    type: string;
+    originalName?: string;
+    originalFormat?: string,
+    originalType?: string;
 }
 
 export interface IFieldInfo {
+    schema?: string,
     fields: IField[],
+    definitions?: any,
 }
 
 export interface IRecentModel {
@@ -434,12 +498,21 @@ export enum FieldType {
     Time = "time",
     Integer = "integer",
     SelectionMark = "selectionMark",
+    Array = "array",
+    Object = "object",
 }
 
 export enum LabelType {
     DrawnRegion = "region"
 }
 
+export enum TableElements {
+    rows = "rows",
+    row = "row",
+    columns = "columns",
+    column = "column",
+}
+
 export enum FieldFormat {
     NotSpecified = "not-specified",
     Currency = "currency",
@@ -463,3 +536,15 @@ export enum ImageMapParent {
     Predict = "predict",
     Editor = "editor",
 }
+
+export enum TagInputMode {
+    Basic = "basic",
+    ConfigureTable = "configureTable",
+    LabelTable = "labelTable",
+}
+
+export enum AnalyzedTagsMode {
+    default = "default",
+    LoadingRecentModel = "loadingRecentModel",
+    ViewTable = "viewTable",
+}
diff --git a/src/providers/storage/azureBlobStorage.ts b/src/providers/storage/azureBlobStorage.ts
index 43060279a..1608ce69d 100644
--- a/src/providers/storage/azureBlobStorage.ts
+++ b/src/providers/storage/azureBlobStorage.ts
@@ -256,7 +256,7 @@ export class AzureBlobStorage implements IStorageProvider {
                 return asset;
             }
         }
-        else{
+        else {
             return null;
         }
     }
diff --git a/src/react/components/common/confirm/confirm.scss b/src/react/components/common/confirm/confirm.scss
new file mode 100644
index 000000000..94e547313
--- /dev/null
+++ b/src/react/components/common/confirm/confirm.scss
@@ -0,0 +1,9 @@
+@import './../../../../assets/sass/theme.scss';
+
+.spinner-container {
+    display: flex;
+    width: 16rem;
+    height: 4rem;
+    align-items: center;
+    justify-content: center;
+}
diff --git a/src/react/components/common/confirm/confirm.tsx b/src/react/components/common/confirm/confirm.tsx
index e58f851a7..48b2ed3d7 100644
--- a/src/react/components/common/confirm/confirm.tsx
+++ b/src/react/components/common/confirm/confirm.tsx
@@ -11,9 +11,12 @@ import {
     ITheme,
     PrimaryButton,
     DefaultButton,
+    SpinnerSize,
+    Spinner
 } from "@fluentui/react";
 import { MessageFormatHandler } from "../messageBox/messageBox";
-import { getDarkTheme } from "../../../../common/themes";
+import { getDarkTheme, getDefaultDarkTheme } from "../../../../common/themes";
+import "./confirm.scss";
 
 /**
  * Properties for Confirm Component
@@ -26,6 +29,7 @@ import { getDarkTheme } from "../../../../common/themes";
 export interface IConfirmProps {
     title?: string;
     message: string | ReactElement<any> | MessageFormatHandler;
+    loadMessage?: string;
     confirmButtonText?: string;
     cancelButtonText?: string;
     confirmButtonTheme?: ITheme;
@@ -40,6 +44,7 @@ export interface IConfirmProps {
 export interface IConfirmState {
     params: any[];
     hideDialog: boolean;
+    loading: boolean;
 }
 
 /**
@@ -54,12 +59,14 @@ export default class Confirm extends React.Component<IConfirmProps, IConfirmStat
         this.state = {
             params: null,
             hideDialog: true,
+            loading: false,
         };
 
         this.open = this.open.bind(this);
         this.close = this.close.bind(this);
         this.onConfirmClick = this.onConfirmClick.bind(this);
         this.onCancelClick = this.onCancelClick.bind(this);
+        this.load = this.load.bind(this)
     }
 
     public render() {
@@ -69,8 +76,9 @@ export default class Confirm extends React.Component<IConfirmProps, IConfirmStat
             },
             scopedSettings: {},
         };
+
         const { confirmButtonTheme } = this.props;
-        const { hideDialog } = this.state;
+        const { hideDialog, loading } = this.state;
 
         return (
             <Customizer {...dark}>
@@ -78,15 +86,26 @@ export default class Confirm extends React.Component<IConfirmProps, IConfirmStat
                     <Dialog
                         hidden={hideDialog}
                         onDismiss={this.close}
-                        dialogContentProps={{
+                        dialogContentProps={!loading ? {
                             type: DialogType.normal,
                             title: this.props.title,
                             subText: this.getMessage(this.props.message),
-                        }}
+                        } : null}
                         modalProps={{
                             isBlocking: true,
                         }}
                     >
+                    {loading && this.props.loadMessage &&
+                        <div className="spinner-container">
+                                    <Spinner
+                                    label={this.props.loadMessage}
+                                    labelPosition="right"
+                                    theme={getDefaultDarkTheme()}
+                                    size={SpinnerSize.large}
+                                />
+                        </div>
+
+                        }
                         <DialogFooter>
                             <PrimaryButton
                                 theme={confirmButtonTheme}
@@ -114,12 +133,20 @@ export default class Confirm extends React.Component<IConfirmProps, IConfirmStat
      * Close Confirm Dialog
      */
     public close(): void {
-        this.setState({ hideDialog: true });
+        this.setState({ hideDialog: true, loading: false });
+    }
+
+    public load(): void {
+        this.setState({loading: true});
     }
 
     private onConfirmClick() {
         this.props.onConfirm.apply(null, this.state.params);
-        this.close();
+        if (this.props.loadMessage) {
+            this.load();
+        } else {
+            this.close();
+        }
     }
 
     private onCancelClick() {
diff --git a/src/react/components/common/documentFilePicker/documentFilePicker.scss b/src/react/components/common/documentFilePicker/documentFilePicker.scss
index c256ac602..c38785a0e 100644
--- a/src/react/components/common/documentFilePicker/documentFilePicker.scss
+++ b/src/react/components/common/documentFilePicker/documentFilePicker.scss
@@ -13,6 +13,7 @@
     }
     .sourceDropdown {
         width: 95px;
+        margin-bottom: 10px;
     }
     .local-file {
         float: right;
diff --git a/src/react/components/common/imageMap/imageMap.tsx b/src/react/components/common/imageMap/imageMap.tsx
index 2da9a4d62..980e67a74 100644
--- a/src/react/components/common/imageMap/imageMap.tsx
+++ b/src/react/components/common/imageMap/imageMap.tsx
@@ -87,7 +87,7 @@ export class ImageMap extends React.Component<IImageMapProps> {
     private modify: Modify;
     private snap: Snap;
 
-    private drawnFeatures: Collection = new Collection([], {unique: true});
+    private drawnFeatures: Collection = new Collection([], { unique: true });
     public modifyStartFeatureCoordinates: any = {};
 
     private imageExtent: number[];
@@ -204,7 +204,7 @@ export class ImageMap extends React.Component<IImageMapProps> {
                 onMouseEnter={this.handlePointerEnterImageMap}
                 className="map-wrapper"
             >
-                <div style={{cursor: this.getCursor()}} id="map" className="map" ref={(el) => this.mapElement = el} />
+                <div style={{ cursor: this.getCursor() }} id="map" className="map" ref={(el) => this.mapElement = el} />
             </div>
         );
     }
@@ -483,7 +483,7 @@ export class ImageMap extends React.Component<IImageMapProps> {
         this.drawRegionVectorLayer?.getSource().clear();
         this.drawnLabelVectorLayer?.getSource().clear();
 
-        this.drawnFeatures = new Collection([], {unique: true});
+        this.drawnFeatures = new Collection([], { unique: true });
 
         this.drawRegionVectorLayer.getSource().on("addfeature", (evt) => {
             this.pushToDrawnFeatures(evt.feature, this.drawnFeatures);
diff --git a/src/react/components/common/tagInput/tableTagConfig.scss b/src/react/components/common/tagInput/tableTagConfig.scss
new file mode 100644
index 000000000..9b2f7a7d4
--- /dev/null
+++ b/src/react/components/common/tagInput/tableTagConfig.scss
@@ -0,0 +1,219 @@
+@import "../../../../assets/sass/theme.scss";
+@import "./tagInputSize.scss";
+
+
+.config-view_container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    padding: 1px;
+    padding-right: 12px;
+    h5, h4 {
+        margin-left: 12px;
+    }
+
+    .table-name_input {
+        width: 100%;
+        padding-right: 10px;
+    }
+
+    .columns_container, .rows_container {
+        margin-top: 2rem;
+        width: 99%;
+        h5 {
+            margin-left: 0;
+        }
+
+        .columns, .rows {
+            width: 99%;
+            margin-right: 2%;
+            background-color: $darker-2;
+        }
+        .ms-DetailsRow-cellCheck {
+            margin-right: -12px;
+            margin-top: 23px;
+        }
+
+        .ms-List-cell:not(:last-child){
+            border-bottom: 1px solid $lighter-4;
+        }
+
+        .column-name_input, .row-name_input {
+            margin-top: 16px;
+        }
+        .input-label-original-name {
+            color: rgb(177, 177, 177);
+            margin-bottom: 2px;
+            margin-right: 2px;
+            text-align: left;
+            margin-top: -24px;
+        }
+    }
+
+    .list_header {
+        width: 100%;
+        .ms-Button-label {
+            margin: 0 -4px;
+        }
+        .list-headers {
+            text-align: left;
+
+        &_name {
+            text-align: left;
+        }
+        &_type {
+            text-align: left;
+            margin: 0 20px;
+        }
+        &_format {
+            text-align: left;
+        }
+    }
+}
+
+    .add_button {
+        margin-top: 8px;
+        margin-left: 0px;
+        width: 140px
+    }
+
+    .control-buttons_container {
+        display: flex;
+        margin-top: 2rem;
+        margin-bottom: 1rem;
+        justify-content: flex-end;
+        .save {
+            margin-left: 1rem;
+        }
+    }
+
+
+    .preview_container {
+        margin-top: 2rem;
+        h5 {
+            margin-left: 0;
+        }
+        .tableName {
+            &-current {
+                padding: 0 1px 2px 1px;
+            }
+            &-original {
+                margin-bottom: 0px;
+                text-decoration: line-through;
+                color: rgb(177, 177, 177);
+                font-size: small;
+            }
+        }
+
+        .table_container {
+            overflow: auto;
+            margin-top: 1rem;
+            .table {
+                td, th {
+                    border: 2px solid #8D8D8D;
+                    color: white;
+                    padding: 0;
+                    margin: 0;
+                }
+                .header {
+                    &_column, &_row {
+                        min-width: 130px;
+                        max-width: 200px;
+                        background-color: $lighter-3;
+                        border: 2px solid grey;
+                        text-align: center;
+                        padding: .125rem .5rem;
+                        .value {
+                        }
+                        .renamed-value {
+                            text-decoration: line-through;
+                            color: rgb(177, 177, 177);
+                            font-size: small;
+                        }
+                    }
+                    &_empty {
+                        border: 2px solid grey;
+                        background-color: $lighter-3;
+                    }
+                }
+                .table-cell {
+                    text-align: center;
+                    background-color: $darker-3;
+                    color: rgba(255, 255, 255, 0.75);
+                    height: 1.5rem;
+                }
+                .hidden {
+                    border: none;
+                    font-size: small;
+                    font-weight: 300;
+                    background-color: transparent;
+                    text-align: right;
+                    vertical-align: middle;
+                    color: rgba(255, 255, 255, 0.45);
+                    width: 3rem;
+                    padding-right: 0.4rem;
+                }
+            }
+        }
+        .rowDynamic_message {
+            margin-bottom: 4px;
+            color: rgba(255, 255, 255, 0.8);
+        }
+    }
+}
+
+
+
+// ========= utility classes
+
+.original-value, .renamed-value {
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+.ml-12px {
+    margin-left: 12px;
+}
+
+  .ms-TextField-errorMessage {
+      padding: 0 0 .25rem .25rem;
+    span {
+        color: #db7272;
+    }
+}
+
+ .ms-DetailsRow-cellCheck {
+    display: flex;
+    width: 22px;
+    height: 28px;
+    margin-right: -12px;
+    margin-top: 6px;
+ }
+
+ .renamed-header-value {
+
+
+ }
+
+ .original-table-name {
+    margin-left: 12px;
+    color: rgb(177, 177, 177);
+    margin-bottom: 2px;
+ }
+
+.restore-button {
+    color: #2d8eac;
+    &:hover {
+        color: #6ac5e1;
+    }
+    &:active {
+        color: #91cee0;
+    }
+}
+
+.compact-row {
+    border-bottom: 0.5px solid $lighter-4;
+ }
+
+.table-name-preview {
+    font-weight: bold;
+}
diff --git a/src/react/components/common/tagInput/tableTagConfig.tsx b/src/react/components/common/tagInput/tableTagConfig.tsx
new file mode 100644
index 000000000..9df097b38
--- /dev/null
+++ b/src/react/components/common/tagInput/tableTagConfig.tsx
@@ -0,0 +1,1153 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+import React, { useEffect, useMemo, useState, useRef } from 'react';
+import { useSelector } from 'react-redux';
+import {
+    Customizer, ICustomizations, ChoiceGroup, IChoiceGroupOption,
+    PrimaryButton, DetailsList, IColumn, TextField, Dropdown, SelectionMode,
+    DetailsListLayoutMode, FontIcon, CheckboxVisibility, IContextualMenuItem,
+    CommandBar, Selection, Separator, IObjectWithKey, ActionButton
+} from "@fluentui/react";
+import { getPrimaryGreyTheme, getPrimaryGreenTheme, getRightPaneDefaultButtonTheme, getGreenWithWhiteBackgroundTheme, getPrimaryBlueTheme, getDefaultTheme } from '../../../../common/themes';
+import { FieldFormat, FieldType, IApplicationState, ITableRegion, ITableTag, ITag, TableElements, TagInputMode, TableVisualizationHint } from '../../../../models/applicationState';
+import { filterFormat, getTagCategory, useDebounce } from "../../../../common/utils";
+import { toast } from "react-toastify";
+import "./tableTagConfig.scss";
+import { interpolate, strings } from "../../../../common/strings";
+import _ from "lodash";
+
+interface ITableTagConfigProps {
+    setTagInputMode: (addTableMode: TagInputMode, selectedTableTagToLabel?: ITableTag, selectedTableTagBody?: ITableRegion[][][]) => void;
+    addTableTag: (table: any) => void;
+    splitPaneWidth: number;
+    tableTag?: ITableTag;
+    reconfigureTableConfirm?: (originalTagName: string, tagName: string, tagType: FieldType.Array | FieldType.Object, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[]) => void;
+    selectedTableBody: ITableRegion[][][];
+}
+
+interface ITableTagConfigState {
+    rows?: ITableConfigItem[],
+    columns: ITableConfigItem[],
+    name: {
+        tableName: string,
+        originalTableName?: string;
+    },
+    type: FieldType.Object | FieldType.Array,
+    format: FieldFormat.NotSpecified,
+    headerTypeAndFormat: TableElements.columns | TableElements.rows;
+    originalName?: string;
+    deletedRows?: ITableConfigItem[],
+    deletedColumns?: ITableConfigItem[],
+}
+interface ITableConfigItem {
+    name: string,
+    format: string,
+    type: string;
+    originalName?: string;
+    originalFormat?: string,
+    originalType?: string;
+    documentCount?: number
+}
+
+const tableFormatOptions: IChoiceGroupOption[] = [
+    {
+        key: FieldType.Object,
+        text: 'Fixed sized',
+        iconProps: { iconName: 'Table' }
+    },
+    {
+        key: FieldType.Array,
+        text: 'Row dynamic',
+        iconProps: { iconName: 'InsertRowsBelow' }
+    },
+];
+const headersFormatAndTypeOptions: IChoiceGroupOption[] = [
+    {
+        key: TableElements.columns,
+        text: 'Column\n fields',
+        iconProps: { iconName: 'TableHeaderRow' }
+    },
+    {
+        key: TableElements.rows,
+        text: 'Row\n fields',
+        iconProps: { iconName: 'TableFirstColumn' }
+    },
+];
+
+const dark: ICustomizations = {
+    settings: {
+        theme: getRightPaneDefaultButtonTheme(),
+    },
+    scopedSettings: {},
+};
+
+const defaultTheme: ICustomizations = {
+    settings: {
+        theme: getDefaultTheme(),
+    },
+    scopedSettings: {},
+};
+
+const formatOptions = (type = FieldType.String) => {
+    const options = [];
+    const formats = filterFormat(type)
+    Object.entries(formats).forEach(([key, value]) => {
+        options.push({ key, text: value })
+    });
+
+    return options;
+};
+
+const typeOptions = () => {
+    const options = [];
+    Object.entries(FieldType).forEach(([key, value]) => {
+        if (value !== FieldType.Object && value !== FieldType.Array) {
+            options.push({ key, text: value });
+        }
+    });
+    return options;
+};
+
+const defaultRowOrColumn = { name: "", type: FieldType.String, format: FieldFormat.NotSpecified, documentCount: 0 };
+
+/**
+ * @name - Table tag configuration
+ * @description - Configures table tag (assigns row's/column's headers and their respective data types and formats)
+ */
+
+export default function TableTagConfig(props: ITableTagConfigProps) {
+    const { setTagInputMode = null, addTableTag = null, splitPaneWidth = null } = props;
+    const containerWidth = splitPaneWidth > 520 ? splitPaneWidth - 10: 510;
+    const inputTableName = useRef(null);
+    const lastColumnInputRef = useRef(null);
+    const lastRowInputRef = useRef(null);
+    // Initial state
+    let table: ITableTagConfigState;
+    if (props.tableTag) {
+        if (props.tableTag?.type === FieldType.Object) {
+            if (props.tableTag.visualizationHint === TableVisualizationHint.Vertical) {
+                table = {
+                    name: {tableName: props.tableTag.name, originalTableName: props.tableTag.name},
+                    type: FieldType.Object,
+                    format: FieldFormat.NotSpecified,
+                    rows: props.tableTag.fields?.map(row => ({ name: row.fieldKey, type: row.fieldType, format: row.fieldFormat, originalName: row.fieldKey, originalFormat: row.fieldFormat, originalType: row.fieldType })) || [defaultRowOrColumn],
+                    columns: props.tableTag?.definition?.fields?.map(col => ({ name: col.fieldKey, type: col.fieldType, format: col.fieldFormat, originalName: col.fieldKey, originalFormat: col.fieldFormat, originalType: col.fieldType })) || [defaultRowOrColumn],
+                    headerTypeAndFormat: TableElements.columns,
+                    deletedColumns: [],
+                    deletedRows: [],
+                }
+
+            } else {
+                table = {
+                    name: {tableName: props.tableTag.name, originalTableName: props.tableTag.name},
+                    type: FieldType.Object,
+                    format: FieldFormat.NotSpecified,
+                    rows: props.tableTag?.definition?.fields?.map(row => ({ name: row.fieldKey, type: row.fieldType, format: row.fieldFormat, originalName: row.fieldKey, originalFormat: row.fieldFormat, originalType: row.fieldType })) || [defaultRowOrColumn],
+                    columns: props.tableTag?.fields?.map(col => ({ name: col.fieldKey, type: col.fieldType, format: col.fieldFormat, originalName: col.fieldKey, originalFormat: col.fieldFormat, originalType: col.fieldType })) || [defaultRowOrColumn],
+                    headerTypeAndFormat: TableElements.rows,
+                    deletedColumns: [],
+                    deletedRows: [],
+                }
+            }
+        } else {
+            table = {
+                name: { tableName: props.tableTag.name, originalTableName: props.tableTag.name },
+                type: FieldType.Array,
+                format: FieldFormat.NotSpecified,
+                rows: [defaultRowOrColumn],
+                columns: props.tableTag?.definition?.fields?.map(col => ({ name: col.fieldKey, type: col.fieldType, format: col.fieldFormat, originalName: col.fieldKey, originalFormat: col.fieldFormat, originalType: col.fieldType  })),
+                headerTypeAndFormat: TableElements.columns,
+                deletedColumns: [],
+            }
+        }
+
+    } else {
+        table = {
+            name: {tableName: ""},
+            type: FieldType.Object,
+            format: FieldFormat.NotSpecified,
+            rows: [defaultRowOrColumn],
+            columns: [defaultRowOrColumn],
+            headerTypeAndFormat: TableElements.columns,
+        };
+    }
+
+    const currentProjectTags = useSelector<ITag[]>((state: IApplicationState) => state.currentProject.tags);
+    const [tableTagName, setTableTagName] = useState(table.name);
+    const [type, setType] = useState<FieldType.Object | FieldType.Array>(table.type);
+    const [format, setFormat] = useState<FieldFormat.NotSpecified>(table.format);
+    const [columns, setColumns] = useState(table.columns);
+    const [rows, setRows] = useState<ITableConfigItem[]>(table.rows);
+    const [notUniqueNames, setNotUniqueNames] = useState<{ columns: [], rows: [], tags: boolean }>({ columns: [], rows: [], tags: false });
+    const [headersFormatAndType, setHeadersFormatAndType] = useState<TableElements.columns | TableElements.rows>(table.headerTypeAndFormat);
+    const [selectedColumn, setSelectedColumn] = useState<IObjectWithKey>(undefined);
+    const [selectedRow, setSelectedRow] = useState<IObjectWithKey>(undefined);
+    const [deletedColumns, setDeletedColumns] = useState(table.deletedColumns);
+    const [deletedRows, setDeletedRows] = useState<ITableConfigItem[]>(table.deletedRows);
+    // const [headerTypeAndFormat, setHeaderTypeAndFormat] = useState<string>(table.headerTypeAndFormat);
+    const [shouldAutoFocus, setShouldAutoFocus] = useState(null);
+
+
+    const isCompatibleWithType = (documentCount: number, type: string, newType: string) => {
+        return documentCount  <= 0 ? true : getTagCategory(type) === getTagCategory(newType);
+    }
+
+    function selectColumnType(idx: number, type: string, docCount: number) {
+        setColumns(columns.map((col, currIdx) => {
+            if (idx === currIdx) {
+                if (isCompatibleWithType(docCount, col.originalType, type))
+                    return { ...col, type, format: FieldFormat.NotSpecified }
+                else {
+                    toast.warn(_.capitalize(interpolate(strings.tags.regionTableTags.configureTag.errors.notCompatibleTableColOrRowType, { kind: "column" })));
+                    return col;
+                }
+            } else {
+                return col
+            }
+        }));
+    }
+
+    function selectRowType(idx: number, type: string, docCount: number) {
+        setRows(rows.map((row, currIdx) => {
+            if (idx === currIdx) {
+                if (isCompatibleWithType(docCount, row.originalType, type))
+                    return { ...row, type, format: FieldFormat.NotSpecified }
+                else {
+                    toast.warn(_.capitalize(interpolate(strings.tags.regionTableTags.configureTag.errors.notCompatibleTableColOrRowType, { kind: "row" })));
+                    return row;
+                }
+            } else {
+                return row
+            }
+        }));
+    }
+
+    function selectColumnFormat(idx: number, format: string) {
+        setColumns(columns.map((col, currIdx) => idx === currIdx ? { ...col, format } : col
+        ));
+    }
+
+    function selectRowFormat(idx: number, format: string) {
+        setRows(rows.map((row, currIdx) => idx === currIdx ? { ...row, format } : row
+        ));
+    }
+
+    const detailListWidth = {
+        nameInput: containerWidth * 0.45,
+        typeInput: containerWidth * 0.176,
+        formatInput: containerWidth * 0.176,
+    }
+
+    const columnListColumns: IColumn[] = [
+        {
+            key: "name",
+            name: "name",
+            fieldName: "name",
+            minWidth: detailListWidth.nameInput,
+            isResizable: false,
+            onRender: (row, index) => {
+                return (
+                    <TextField
+                        componentRef={(index === columns.length - 1 && index !== 0) ? lastColumnInputRef : null}
+                        className={"column-name_input"}
+                        theme={getGreenWithWhiteBackgroundTheme()}
+                        onChange={(e) => handleTextInput(e.target["value"], TableElements.column, index)}
+                        value={row.name}
+                        errorMessage={getTextInputError(notUniqueNames.columns, row.name.trim(), index)}
+                        onRenderLabel={() => {
+                            return row.originalName ?
+                                <div className={"input-label-original-name original-value"}>
+                                    Original name: {row.originalName}
+                                </div>
+                                : null;
+                        }}
+                    />
+                )
+            },
+        },
+        {
+            key: "type",
+            name: "type",
+            fieldName: "type",
+            minWidth: detailListWidth.typeInput,
+            isResizable: false,
+            onRender: (row, index) => headersFormatAndType === TableElements.columns ?
+                <Customizer {...defaultTheme}>
+                    <Dropdown
+                        style={{ marginTop: 16 }}
+                        className="type_dropdown"
+                        placeholder={row.type}
+                        defaultSelectedKey={FieldType.String}
+                        options={typeOptions()}
+                        theme={getGreenWithWhiteBackgroundTheme()}
+                        onChange={(e, val) => selectColumnType(index, val.text, row.documentCount)}
+                    />
+                </Customizer>
+                : <></>
+        },
+        {
+            key: "format",
+            name: "format",
+            fieldName: "format",
+            minWidth: detailListWidth.formatInput,
+            isResizable: false,
+            onRender: (row, index) => headersFormatAndType === TableElements.columns ?
+                <Customizer {...defaultTheme}>
+                    <Dropdown
+                        style={{ marginTop: 16 }}
+                        className="format_dropdown"
+                        placeholder={row.format}
+                        selectedKey={row.format}
+                        options={formatOptions(row.type)}
+                        theme={getGreenWithWhiteBackgroundTheme()}
+                        onChange={(e, val) => {
+                            selectColumnFormat(index, val.text);
+                        }}
+                    />
+                </Customizer>
+                : <></>
+        },
+    ];
+
+    const rowListColumns: IColumn[] = [
+        {
+            key: "name",
+            name: "name",
+            fieldName: "name",
+            minWidth: detailListWidth.nameInput,
+            isResizable: false,
+            onRender: (row, index) => {
+                return (
+                    <TextField
+                        componentRef={(index === rows.length - 1 && index !== 0) ? lastRowInputRef : null}
+                        className="row-name_input"
+                        theme={getGreenWithWhiteBackgroundTheme()}
+                        onChange={(e) => handleTextInput(e.target["value"], TableElements.row, index)}
+                        value={row.name}
+                        errorMessage={getTextInputError(notUniqueNames.rows, row.name, index)}
+                        onRenderLabel={() => {
+                            return row.originalName ?
+                                <div className={"input-label-original-name original-value"}>
+                                    Original name: {row.originalName}
+                                </div>
+                                : null;
+                        }}
+                    />
+                )
+            },
+        },
+        {
+            key: "type",
+            name: "type",
+            fieldName: "type",
+            minWidth: detailListWidth.typeInput,
+            isResizable: false,
+            onRender: (row, index) => headersFormatAndType === TableElements.rows ?
+                <Customizer {...defaultTheme}>
+                    <Dropdown
+                        className="type_dropdown"
+                        style={{ marginTop: 16 }}
+                        placeholder={row.type}
+                        defaultSelectedKey={FieldType.String}
+                        options={typeOptions()}
+                        theme={getGreenWithWhiteBackgroundTheme()}
+                        onChange={(e, val) => selectRowType(index, val.text, row.documentCount)}
+                    />
+                </Customizer>
+                : <></>
+        },
+        {
+            key: "format",
+            name: "format",
+            fieldName: "format",
+            minWidth: detailListWidth.formatInput,
+            isResizable: false,
+            onRender: (row, index) => headersFormatAndType === TableElements.rows ?
+                <Customizer {...defaultTheme}>
+                    <Dropdown
+                        className="format_dropdown"
+                        style={{ marginTop: 16 }}
+                        placeholder={row.format}
+                        selectedKey={row.format}
+                        options={formatOptions(row.type)}
+                        theme={getGreenWithWhiteBackgroundTheme()}
+                        onChange={(e, val) => {
+                            selectRowFormat(index, val.text);
+                        }}
+                    />
+                </Customizer>
+                : <></>
+        },
+    ];
+
+
+    function addColumn() {
+        setColumns([...columns, defaultRowOrColumn]);
+        setShouldAutoFocus(TableElements.column);
+    }
+
+    function addRow() {
+        setRows([...rows, defaultRowOrColumn]);
+        setShouldAutoFocus(TableElements.row);
+    }
+
+    function handleTextInput(name: string, role: string, index: number) {
+        if (role === TableElements.column) {
+            setColumns(
+                columns.map((column, currIndex) => (index === currIndex)
+                    ? { ...column, name }
+                    : column)
+            );
+        } else {
+            setRows(
+                rows.map((row, currIndex) => (index === currIndex) ?
+                    { ...row, name }
+                    : row)
+            );
+        };
+    }
+
+    function setTableName(name: string) {
+        setTableTagName({...tableTagName,tableName: name});
+    }
+
+    // Row/Column headers command bar (reorder, delete)
+    function getRowsHeaderItems(): IContextualMenuItem[] {
+        const currSelectionIndex = rowSelection.getSelectedIndices()[0];
+        return [
+            {
+                key: 'Name',
+                text: 'Name',
+                className: "list-headers_name",
+                style: { width: detailListWidth.nameInput - 122 },
+                disabled: true,
+            },
+            {
+                key: 'moveUp',
+                text: 'Move up',
+                iconOnly: true,
+                iconProps: { iconName: 'Up' },
+                onClick: (e) => {
+                    onReOrder(-1, TableElements.rows)
+                },
+                disabled: !selectedRow || currSelectionIndex === 0,
+            },
+            {
+                key: 'moveDown',
+                text: 'Move down',
+                iconOnly: true,
+                iconProps: { iconName: 'Down' },
+                onClick: (e) => {
+                    onReOrder(1, TableElements.rows)
+                },
+                disabled: !selectedRow! || currSelectionIndex === rows.length - 1,
+            },
+            {
+                key: 'deleteRow',
+                text: 'Delete row',
+                iconOnly: true,
+                iconProps: { iconName: 'Delete' },
+                onClick: () => {
+                    const selectedRowIndex =  rowSelection.getSelectedIndices()[0];
+                    if (props.tableTag && rows[selectedRowIndex].originalName) {
+                        const deletedRow = Object.assign({}, rows[selectedRowIndex]);
+                        deletedRow.name = deletedRow.originalName;
+                        deletedRow.format = deletedRow.originalFormat;
+                        deletedRow.type = deletedRow.originalType;
+                        setDeletedRows([...deletedRows, deletedRow]);
+                    }
+                    setRows(rows.filter((i, idx) => idx !== rowSelection.getSelectedIndices()[0]))
+                },
+                disabled: !selectedRow! || rows.length === 1,
+            },
+            {
+                key: 'type',
+                text: 'Type',
+                className: "list-headers_type",
+                style: { width: detailListWidth.typeInput },
+                disabled: true,
+            },
+            {
+                key: 'format',
+                text: 'Format',
+                className: "list-headers_format",
+                disabled: true,
+            },
+        ];
+    };
+    function getColumnsHeaderItems(): IContextualMenuItem[] {
+        const currSelectionIndex = columnSelection.getSelectedIndices()[0];
+
+        return [
+            {
+                key: 'Name',
+                text: 'Name',
+                className: "list-headers_name",
+                style: { width: detailListWidth.nameInput - 120 },
+                disabled: true,
+                resizable: true,
+            },
+            {
+                key: 'moveUp',
+                text: 'Move up',
+                iconOnly: true,
+                iconProps: { iconName: 'Up' },
+                onClick: (e) => {
+                    onReOrder(-1, TableElements.columns)
+
+                },
+                disabled: !selectedColumn || currSelectionIndex === 0,
+            },
+            {
+                key: 'moveDown',
+                text: 'Move down',
+                iconOnly: true,
+                iconProps: { iconName: 'Down' },
+                onClick: (e) => {
+                    onReOrder(1, TableElements.columns)
+                },
+                disabled: !selectedColumn || currSelectionIndex === columns.length - 1,
+            },
+            {
+                key: 'deleteColumn',
+                text: 'Delete column',
+                iconOnly: true,
+                iconProps: { iconName: 'Delete', },
+                onClick: () => {
+                    const selectedColumnIndex = columnSelection.getSelectedIndices()[0];
+                    if (props.tableTag && columns[selectedColumnIndex].originalName) {
+                        const deletedColumn = Object.assign({}, columns[selectedColumnIndex]);
+                        deletedColumn.name = deletedColumn.originalName
+                        deletedColumn.format = deletedColumn.originalFormat;
+                        deletedColumn.type = deletedColumn.originalType;
+                        setDeletedColumns([...deletedColumns, deletedColumn])
+                    }
+                    setColumns(columns.filter((i, idx) => idx !== selectedColumnIndex));
+                },
+                disabled: !selectedColumn || columns.length === 1,
+            },
+            {
+                key: 'type',
+                text: 'Type',
+                className: "list-headers_type",
+                style: { width: detailListWidth.typeInput },
+                disabled: true,
+            },
+            {
+                key: 'format',
+                text: 'Format',
+                className: "list-headers_format",
+                disabled: true,
+            },
+        ];
+    };
+
+    // Validation //
+    function getTextInputError(array: any[], rowName: string, index: number) {
+        if (!rowName?.length) {
+            return strings.tags.regionTableTags.configureTag.errors.emptyName
+        } else if (array.length && array.findIndex((item) => (item === index)) !== -1) {
+            return strings.tags.regionTableTags.configureTag.errors.notUniqueName;
+        } else {
+            return undefined;
+        }
+    };
+
+    function checkNameUniqueness(array: ITableConfigItem[], arrayName: string) {
+        const namesMap = {};
+        let notUniques = [];
+        array.forEach((item, idx) => {
+
+            if (item.name && item.name.length) {
+                const name = item.name.trim();
+                namesMap[name] = namesMap[name] || [];
+                namesMap[name].push(idx)
+            }
+        });
+
+        for (const name in namesMap) {
+            if (namesMap[name].length > 1) {
+                notUniques = namesMap[name];
+            }
+        }
+        setNotUniqueNames({ ...notUniqueNames, [arrayName]: notUniques })
+    }
+
+    // Check names uniqueness for rows and columns as you type , with a delay
+    const delay = 400;
+    const debouncedColumns = useDebounce(columns, delay);
+    const debouncedRows = useDebounce(rows, delay);
+
+    useEffect(() => {
+        if (columns) {
+            checkNameUniqueness(debouncedColumns, TableElements.columns)
+        }
+    }, [debouncedColumns]);
+
+    useEffect(() => {
+        if (rows) {
+            checkNameUniqueness(debouncedRows, TableElements.rows);
+        }
+    }, [debouncedRows]);
+
+    // Check tableName uniqueness as type
+    const debouncedTableTagName = useDebounce(tableTagName, delay);
+
+    useEffect(() => {
+        if (tableTagName) {
+            const existingTagName = currentProjectTags.find((item: ITag) => item.name === tableTagName.tableName.trim());
+            setNotUniqueNames({ ...notUniqueNames, tags: existingTagName !== undefined ? true : false })
+        }
+    }, [debouncedTableTagName, currentProjectTags]);
+
+    function trimFieldNames(array: ITableConfigItem[]) {
+        return array.map(i => ({ ...i, name: i.name.trim() }));
+    }
+
+    function save(cleanTableName: string, cleanRows: ITableConfigItem[], cleanColumns: ITableConfigItem[]) {
+        const [ firstLayerFieldsInput, secondLayerFieldsInput ] = getFieldsLayersInput(headersFormatAndType, cleanRows, cleanColumns);
+        const definition = getDefinitionLayer(cleanTableName, secondLayerFieldsInput);
+        const fieldsLayer = getFieldsLayer(cleanTableName, firstLayerFieldsInput);
+        const itemType = getItemType(cleanTableName);
+        const visualizationHint = getVisualizationHint(headersFormatAndType);
+        const tableTagToAdd = {
+            name: cleanTableName,
+            type,
+            columns: cleanColumns,
+            format : FieldFormat.NotSpecified,
+            itemType,
+            fields: fieldsLayer,
+            definition,
+            visualizationHint,
+        }
+        if (type === FieldType.Object) {
+            tableTagToAdd[TableElements.rows] = cleanRows;
+        }
+        addTableTag(tableTagToAdd);
+        setTagInputMode(TagInputMode.Basic, null, null);
+        toast.success(`Successfully ${props.tableTag ? "reconfigured" : "saved"} "${tableTagName.tableName}" table tag.`, { autoClose: 8000 });
+    }
+
+    function getItemType(cleanTableName) {
+        if (type === FieldType.Object) {
+            return null;
+        } else {
+            return cleanTableName + "_object";
+        }
+    }
+
+    function getFieldsLayersInput(headersFormatAndType, cleanRows, cleanColumns) {
+        if (type === FieldType.Object) {
+            if (headersFormatAndType === TableElements.columns) {
+                return [ cleanRows, cleanColumns ];
+            } else {
+                return [ cleanColumns, cleanRows ];
+            }
+        } else {
+            return [ cleanRows, cleanColumns ];
+        }
+    }
+
+    function getDefinitionLayer(cleanTableName, secondLayerFieldsInput) {
+        return {
+            fieldKey: cleanTableName + "_object",
+            fieldType: FieldType.Object,
+            fieldFormat: FieldFormat.NotSpecified,
+            itemType: null,
+            fields: secondLayerFieldsInput.map((field) => {
+                return {
+                    fieldKey: field.name,
+                    fieldType: field.type,
+                    fieldFormat: field.format,
+                    itemType: null,
+                    fields: null,
+                }
+            })
+        }
+    }
+
+    function getVisualizationHint(headersFormatAndType) {
+        if (type === FieldType.Object) {
+            if (headersFormatAndType === TableElements.columns) {
+                return TableVisualizationHint.Vertical;
+            } else {
+                return TableVisualizationHint.Horizontal;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    function getFieldsLayer(cleanTableName, firstLayerFieldsInput) {
+        if (type === FieldType.Object) {
+            return firstLayerFieldsInput.map((field) => {
+                return {
+                    fieldKey: field.name,
+                    fieldType:  cleanTableName + "_object",
+                    fieldFormat: FieldFormat.NotSpecified,
+                    itemType: null,
+                    fields: null,
+                }
+            });
+        } else {
+            return null;
+        }
+    }
+
+    function hasEmptyNames(array: ITableConfigItem[]) {
+        return array.find((i) => !i.name.length) !== undefined ? true : false
+    }
+
+
+
+    function getCleanTable() {
+        let cleanRows = rows;
+        let cleanColumns = columns;
+        if (headersFormatAndType === TableElements.columns) {
+            cleanRows = rows.map((row) => {
+                return {
+                    ...row,
+                    type: FieldType.String,
+                    format: FieldFormat.NotSpecified,
+                }
+            });
+        } else if (headersFormatAndType === TableElements.rows) {
+            cleanColumns = columns.map((col) => ({
+                ...col,
+                type: FieldType.String,
+                format: FieldFormat.NotSpecified
+            }));
+        }
+        cleanColumns = trimFieldNames(columns);
+        if (type === FieldType.Object) {
+            cleanRows = trimFieldNames(rows);
+        }
+        const cleanTableName = tableTagName.tableName.trim();
+        // const cleanOriginalTableName = tableTagName?.originalTableName?.trim();
+        return { cleanTableName, cleanRows, cleanColumns };
+    }
+
+    function validateInput() {
+        return !(
+            notUniqueNames.rows.length > 0
+            || notUniqueNames.columns.length > 0
+            || (props.tableTag && notUniqueNames.tags && (tableTagName.tableName !== tableTagName.originalTableName))
+            || (notUniqueNames.tags && !props.tableTag)
+            || !tableTagName.tableName.length
+            || hasEmptyNames(columns)
+            || (type === FieldType.Object && hasEmptyNames(rows))
+        );
+    }
+
+    // Row selection
+    const rowSelection = useMemo(() =>
+        new Selection({
+            onSelectionChanged: () => {
+                setSelectedRow(rowSelection.getSelection()[0])
+            }, selectionMode: SelectionMode.single,
+        }), []
+    );
+
+    const columnSelection = useMemo(() =>
+        new Selection({
+            onSelectionChanged: () => {
+                setSelectedColumn(columnSelection.getSelection()[0])
+            }, selectionMode: SelectionMode.single,
+        }), []
+    );
+
+    // Reorder items
+    function onReOrder(displacement: number, role: string) {
+        const items = role === TableElements.rows ? [...rows] : [...columns];
+        const selection = role === TableElements.rows ? rowSelection : columnSelection;
+        const selectedIndex = selection.getSelectedIndices()[0];
+        const itemToBeMoved = items[selectedIndex];
+        const newIndex = selectedIndex + displacement;
+        if (newIndex < 0 || newIndex > items.length - 1) {
+            return;
+        }
+
+        items.splice(selectedIndex, 1);
+        items.splice(newIndex, 0, itemToBeMoved);
+
+        if (role === TableElements.rows) {
+            rowSelection.setIndexSelected(newIndex, true, false);
+            setRows(items);
+        } else {
+            columnSelection.setIndexSelected(newIndex, true, true);
+            setColumns(items);
+        }
+    }
+
+    function restoreDeletedField(fieldType: TableElements, index: number) {
+        let fields;
+        let deletedFields;
+        let setFields;
+        let setDeletedFields;
+        switch (fieldType) {
+            case TableElements.row:
+                fields = rows;
+                deletedFields = [...deletedRows];
+                setFields = setRows;
+                setDeletedFields = setDeletedRows;
+                break;
+            case TableElements.column:
+                fields = columns;
+                deletedFields = [...deletedColumns];
+                setFields = setColumns;
+                setDeletedFields = setDeletedColumns;
+                break;
+        }
+        setFields([...fields, deletedFields[index]]);
+        setDeletedFields([...deletedFields].slice(0, index).concat([...deletedFields].slice(index+1, deletedFields.length)));
+    }
+
+    function getDeletedFieldsTable(fieldType: TableElements) {
+        let deletedFields;
+        switch (fieldType) {
+            case TableElements.row:
+                deletedFields = deletedRows;
+                break;
+            case TableElements.column:
+                deletedFields = deletedColumns;
+                break;
+        }
+        const tableBody = [[
+            <tr className="compact-row" key={"row-h"}>
+                <th key={"row-h-0"} className="">
+                    <div className="mr-4">
+                        {fieldType.charAt(0).toUpperCase() + fieldType.slice(1) + " fields that'll be deleted"}
+                    </div>
+                </th>
+                <th key={"row-h-1"} className=""></th>
+            </tr>
+        ]];
+        for (let i = 0; i < deletedFields.length; i++) {
+            tableBody.push([
+                <tr className="compact-row" key={`row-${i}`}>
+                    <td key={`cell-${i}-0`} className="">
+                        <div className="flex-center">
+                            {deletedFields[i].originalName}
+                        </div>
+                    </td>
+                    <td key={`cell-${i}-1`} className="">
+                        <ActionButton className="restore-button flex-center"
+                            onClick={() => {
+                                restoreDeletedField(fieldType, i)
+                            }}>
+                            <FontIcon className="restore-icon mr-1" iconName="UpdateRestore" />
+                            Restore
+                        </ActionButton>
+                    </td>
+                </tr>
+            ])
+        }
+        return tableBody;
+    }
+
+    // Table preview
+    function getTableBody() {
+        let tableBody = null;
+        const isRowDynamic = type === FieldType.Array;
+        if (table.rows.length !== 0 && table.columns.length !== 0) {
+            tableBody = [];
+            for (let i = 0; i < (isRowDynamic ? 2 : rows.length + 1); i++) {
+                const tableRow = [];
+                for (let j = 0; j < columns.length + 1; j++) {
+                    if (i === 0 && j !== 0) {
+                        const columnHeaderWasRenamed = props.tableTag && columns[j - 1].name !== columns[j - 1].originalName;
+                        tableRow.push(
+                            <th key={`col-h-${j}`} className="header_column">
+                                {columnHeaderWasRenamed &&
+                                    <div className="renamed-value">{columns[j - 1].originalName}</div>
+                                }
+                                <div className="original-value">
+                                    {columns[j - 1].name}
+                                </div>
+                            </th>);
+                    } else if (j === 0 && i !== 0) {
+                        if (!isRowDynamic) {
+                            const rowHeaderWasRenamed = props.tableTag && rows[i - 1].name !== rows[i - 1].originalName;
+                            tableRow.push(
+                                <th key={`row-h-${j}`} className="header_row">
+                                    {rowHeaderWasRenamed &&
+                                        <div className="renamed-value">
+                                            {rows[i - 1].originalName}
+                                        </div>
+                                    }
+                                    <div className="original-value">
+                                        {rows[i - 1].name}
+                                    </div>
+                                </th>
+                            );
+                        }
+                    } else if (j === 0 && i === 0) {
+                        if (!isRowDynamic) {
+                            tableRow.push(<th key={"ignore"} className="header_empty" ></th>);
+                        }
+                    } else {
+                        tableRow.push(<td key={`cell-${i}-${j}`} className="table-cell" />);
+                    }
+                }
+                tableBody.push(<tr key={`row-${i}`}>{tableRow}</tr>);
+            }
+        }
+        return tableBody
+    };
+
+
+    function getTableTagNameErrorMessage(): string {
+        if (props.tableTag && tableTagName.tableName.trim() === props.tableTag.name) {
+            return "";
+        } else if (!tableTagName.tableName.trim().length) {
+            return strings.tags.regionTableTags.configureTag.errors.assignTagName
+        } else if (notUniqueNames.tags) {
+            return strings.tags.regionTableTags.configureTag.errors.notUniqueTagName;
+        }
+        return "";
+    }
+
+    const [tableChanged, setTableChanged] = useState<boolean>(false);
+    useEffect(() => {
+        setTableChanged(
+            (_.isEqual(columns, table.columns) && _.isEqual(rows, table.rows)) ? false : true)
+    }, [columns, rows, table.columns, table.rows]);
+
+    // Focus once on table name input when the component loads
+    useEffect(() => {
+        inputTableName.current.focus();
+    }, []);
+    // Sets focus on last added input
+    useEffect(() => {
+        if (shouldAutoFocus === TableElements.column && lastColumnInputRef.current) {
+            lastColumnInputRef.current.focus();
+        }
+        else if (shouldAutoFocus === TableElements.row && lastRowInputRef.current) {
+            lastRowInputRef.current.focus();
+        }
+        setShouldAutoFocus(null);
+    }, [shouldAutoFocus]);
+
+    // Render
+    return (
+        <Customizer {...dark}>
+            <div className="config-view_container">
+                <h4 className="mt-2">{props.tableTag ? "Reconfigure table tag" : "Configure table tag"}</h4>
+                <h5 className="mt-3 mb-1">Name</h5>
+                {tableTagName.originalTableName &&
+                    <div className={"original-table-name"}>
+                        Original name: {tableTagName.originalTableName}
+                    </div>
+                }
+                <TextField
+                    componentRef={inputTableName}
+                    className="table-name_input ml-12px"
+                    theme={getGreenWithWhiteBackgroundTheme()}
+                    onChange={(event) => setTableName(event.target["value"])}
+                    value={tableTagName.tableName}
+                    errorMessage={getTableTagNameErrorMessage()}
+                />
+                {!props.tableTag &&
+                    <>
+                        <h5 className="mt-4">Type</h5>
+                        <ChoiceGroup
+                            className="ml-12px"
+                            onChange={(event, option) => {
+                                if (option.key === FieldType.Object) {
+                                    setType(FieldType.Object);
+                                } else {
+                                    setType(FieldType.Array)
+                                    setHeadersFormatAndType(TableElements.columns);
+                                }
+                            }}
+                            defaultSelectedKey={FieldType.Object}
+                            options={tableFormatOptions}
+                            theme={getRightPaneDefaultButtonTheme()}
+                        />
+                        {type === FieldType.Object && <>
+                            <h5 className="mt-4" >Configure type and format for:</h5>
+                            <ChoiceGroup
+                                className="ml-12px type-format"
+                                defaultSelectedKey={TableElements.columns}
+                                options={headersFormatAndTypeOptions}
+                                onChange={(e, option) => {
+                                    if (option.key === TableElements.columns) {
+                                        setHeadersFormatAndType(TableElements.columns)
+
+                                    } else {
+                                        setHeadersFormatAndType(TableElements.rows)
+                                    }
+                                }}
+                                required={false} />
+                        </>
+                        }
+                    </>
+                }
+                <div className="columns_container mb-4 ml-12px">
+                    <h5 className="mt-3">Column fields</h5>
+                    <div className="columns-list_container">
+                        <DetailsList
+                            className="columns"
+                            items={columns}
+                            columns={columnListColumns}
+                            isHeaderVisible={true}
+                            theme={getRightPaneDefaultButtonTheme()}
+                            compact={false}
+                            setKey="none"
+                            selection={columnSelection}
+                            layoutMode={DetailsListLayoutMode.justified}
+                            checkboxVisibility={CheckboxVisibility.always}
+                            onRenderDetailsHeader={() => (
+                                <div className="list_header">
+                                    <CommandBar items={getColumnsHeaderItems()} />
+                                    <Separator styles={{ root: { height: 2, padding: 0 } }} />
+                                </div>
+                            )}
+
+                        />
+                    </div>
+                    <PrimaryButton
+                        theme={getPrimaryBlueTheme()}
+                        className="add_button ml-12px"
+                        onClick={addColumn}>
+                        <FontIcon iconName="Add" className="mr-2" />
+                    Add column
+                </PrimaryButton>
+                {deletedColumns?.length > 0 &&
+                <div className="mt-3">
+                    <table className="">
+                            <tbody>
+                                {getDeletedFieldsTable(TableElements.column)}
+                            </tbody>
+                        </table>
+                    </div>
+                }
+                </div>
+                {((props.tableTag?.type === FieldType.Object) || type === FieldType.Object) &&
+                    <div className="rows_container mb-4 ml-12px">
+                        <h5 className="">Row fields</h5>
+                        <div className="rows-list_container">
+                            <DetailsList
+                                className="rows"
+                                items={rows}
+                                columns={rowListColumns}
+                                isHeaderVisible={true}
+                                theme={getRightPaneDefaultButtonTheme()}
+                                compact={false}
+                                setKey="none"
+                                selection={rowSelection}
+                                layoutMode={DetailsListLayoutMode.justified}
+                                checkboxVisibility={CheckboxVisibility.always}
+                                selectionPreservedOnEmptyClick={true}
+                                onRenderDetailsHeader={() => (
+                                    <div className="list_header">
+                                        <CommandBar items={getRowsHeaderItems()} />
+                                        <Separator styles={{ root: { height: 2, padding: 0 } }} />
+                                    </div>
+                                )}
+                            />
+                        </div>
+                        <PrimaryButton
+                            theme={getPrimaryBlueTheme()}
+                            className="add_button"
+                            onClick={addRow}>
+                            <FontIcon iconName="Add" className="mr-2" />
+                                Add row
+                            </PrimaryButton>
+                            {deletedRows?.length > 0 &&
+                                <div className="mt-3">
+                                    <table className="">
+                                        <tbody>
+                                            {getDeletedFieldsTable(TableElements.row)}
+                                        </tbody>
+                                    </table>
+                                </div>
+                            }
+                    </div>
+                }
+                {
+                    (tableChanged || props.tableTag) &&
+                    <div className="preview_container  ml-12px">
+                        <h5 className="mt-3 mb-1">Preview</h5>
+                        {tableTagName.tableName &&
+                            <>
+                                {props.tableTag && tableTagName.originalTableName !== tableTagName.tableName &&
+                                    <div className="tableName-original original-value">
+                                        Table name: {tableTagName.originalTableName}
+                                    </div>
+                                }
+                            <span className="tableName-current"
+                                style={{ borderBottom: props.tableTag ? `4px solid ${props.tableTag.color}` : null }}>
+                                    <span>Table name: </span>
+                                    <span className="table-name-preview">{tableTagName.tableName}</span>
+                                </span>
+                            </>
+                        }
+                        {
+                            type === FieldType.Array && <div className="rowDynamic_message">The number of rows is specified when labeling each document.</div>
+                        }
+                        <div className="table_container">
+                            <table className="table">
+                                <tbody>
+                                    {getTableBody()}
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                }
+                <div className="control-buttons_container">
+                    <PrimaryButton
+                        className="cancel"
+                        theme={getPrimaryGreyTheme()}
+                        onClick={() => {
+                            if (props.tableTag) {
+                                if (props.selectedTableBody) {
+                                    if (!props.selectedTableBody[0].length || props.selectedTableBody.length !== 0)
+                                        setTagInputMode(TagInputMode.LabelTable,
+                                            props.tableTag,
+                                            props.selectedTableBody);
+                                } else {
+                                    setTagInputMode(TagInputMode.Basic, null, null);
+                                }
+                            } else {
+                                setTagInputMode(TagInputMode.Basic, null, null);
+                            }
+                        }}>Cancel</PrimaryButton>
+                    <PrimaryButton
+                        className="save"
+                        theme={getPrimaryGreenTheme()}
+                        onClick={() => {
+                            if (!validateInput()) {
+                                toast.error(strings.tags.regionTableTags.configureTag.errors.checkFields, { autoClose: 8000 });
+                                return;
+                            } else {
+                                const { cleanTableName, cleanRows, cleanColumns} = getCleanTable();
+                                if (props.tableTag) {
+                                    const tableTagToReconfigure = {
+                                        name: cleanTableName,
+                                        columns: cleanColumns,
+                                        deletedColumns,
+                                        headersFormatAndType,
+                                        visualizationHint: props.tableTag.visualizationHint,
+                                    }
+                                    if (type === FieldType.Object) {
+                                        tableTagToReconfigure[TableElements.rows] = cleanRows;
+                                        tableTagToReconfigure["deletedRows"] = deletedRows;
+                                        tableTagToReconfigure["type"] = FieldType.Object;
+                                    } else {
+                                        tableTagToReconfigure[TableElements.rows] = null;
+                                        tableTagToReconfigure["deletedRows"] = null;
+                                        tableTagToReconfigure["type"] = FieldType.Array;
+                                    }
+                                    props.reconfigureTableConfirm(tableTagName?.originalTableName?.trim(), tableTagName?.tableName?.trim(), tableTagToReconfigure["type"], tableTagToReconfigure["format"], tableTagToReconfigure.visualizationHint, deletedColumns, deletedRows, tableTagToReconfigure["rows"], tableTagToReconfigure.columns);
+                                } else {
+                                    save(cleanTableName, cleanRows, cleanColumns);
+                                }
+                            }
+                        }
+                        }>Save</PrimaryButton>
+                </div>
+            </div>
+        </Customizer>
+    );
+};
diff --git a/src/react/components/common/tagInput/tableTagLabeling.scss b/src/react/components/common/tagInput/tableTagLabeling.scss
new file mode 100644
index 000000000..b5fe584ef
--- /dev/null
+++ b/src/react/components/common/tagInput/tableTagLabeling.scss
@@ -0,0 +1,91 @@
+
+@import "../../../../assets/sass/theme.scss";
+
+.table-labeling_container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    max-width: 100%;
+    padding-right: .5rem;
+    h4 {
+        padding-left: 1.25rem;
+    }
+    .labeling-guideline {
+        margin: 1rem 1rem 2rem 1.5rem;
+        color: rgba(255, 255, 255, 0.6);
+        }
+        .table-name {
+            padding-left: 1.25rem;
+            span {
+                padding-bottom: .2rem;
+            }
+        }
+        .add-row-button_container {
+            margin-left: 1.5rem;
+            margin-bottom: 3rem;
+            margin-top: 1rem;
+        }
+        .table-view-container {
+            overflow-x: auto;
+        .viewed-table {
+            margin-bottom: 1rem;
+            .column_header {
+                text-overflow: ellipsis;
+                overflow: hidden;
+                min-width: 130px;
+                max-width: 200px;
+                background-color: $lighter-3;
+                border: 2px solid grey;
+                text-align: center;
+                padding: .125rem .25rem;
+            }
+            .row_header {
+                text-overflow: ellipsis;
+                overflow: hidden;
+                min-width: 130px;
+                max-width: 200px;
+                border: 2px solid grey;
+                background-color: $lighter-3;
+                text-align: center;
+                padding: .125rem .5rem;
+            }
+            .empty_header {
+                border: 2px solid grey;
+                background-color: $lighter-3;
+
+            }
+            .table-cell {
+                text-align: center;
+                background-color: $darker-3;
+                color: rgba(255, 255, 255, 0.75);
+                &:hover {
+                    background-color: $lighter-1;
+                }
+                &:active {
+                    background-color: $lighter-2;
+
+                }
+            }
+            .hidden {
+                border: none;
+                background-color: transparent;
+                text-align: center;
+                color: rgba(255, 255, 255, 0.45);
+                min-width: 12px;
+                max-width: 48px;
+                padding-right: 0.3rem;
+            }
+        }
+    }
+
+    .buttons-container {
+        display: flex;
+        padding: 1.5rem;
+        justify-content: space-between;
+        .button {
+            width: 160px;
+            &-reconfigure {
+            }
+        }
+    }
+}
diff --git a/src/react/components/common/tagInput/tableTagLabeling.tsx b/src/react/components/common/tagInput/tableTagLabeling.tsx
new file mode 100644
index 000000000..3ef521adf
--- /dev/null
+++ b/src/react/components/common/tagInput/tableTagLabeling.tsx
@@ -0,0 +1,171 @@
+import React from 'react';
+import "./tableTagConfig.scss";
+import { PrimaryButton, FontIcon, DefaultButton } from "@fluentui/react";
+import { getPrimaryGreenTheme, getPrimaryBlueTheme } from '../../../../common/themes';
+import { FieldFormat, FieldType, TagInputMode, IRegion, ITableTag, ITableRegion, IField, TableElements, ITableField, ITableKeyField, TableVisualizationHint } from '../../../../models/applicationState';
+import "./tableTagLabeling.scss";
+
+import { strings } from "../../../../common/strings";
+
+
+interface ITableTagLabelingProps {
+    setTagInputMode: (addTableMode: TagInputMode, selectedTableTagToLabel?: ITableTag, selectedTableTagBody?: ITableRegion[][][]) => void;
+    selectedTag: ITableTag,
+    selectedRegions?: IRegion[];
+    onTagClick?: (tag: ITableTag) => void;
+    selectedTableTagBody: ITableRegion[][][];
+    handleTableCellClick: (iTableCellIndex: number, jTableCellIndex: number) => void;
+    handleTableCellMouseEnter: (regions: IRegion[]) => void
+    handleTableCellMouseLeave: () => void
+    addRowToDynamicTable: () => void;
+    splitPaneWidth?: number;
+}
+
+
+interface ITableTagLabelingState {
+    selectedRowIndex: number;
+    selectedColumnIndex: number;
+    rows: ITableKeyField[],
+    columns: ITableKeyField[],
+    selectedTableTagBody: any,
+}
+
+// @connect(mapStateToProps)
+export default class TableTagLabeling extends React.Component<ITableTagLabelingProps> {
+    public state: ITableTagLabelingState = {
+        selectedRowIndex: null,
+        selectedColumnIndex: null,
+        rows: this.props.selectedTag.type === FieldType.Array || this.props.selectedTag?.visualizationHint === TableVisualizationHint.Vertical ? this.props.selectedTag.fields : this.props.selectedTag.definition.fields,
+        columns: this.props.selectedTag.type === FieldType.Array || this.props.selectedTag.visualizationHint === TableVisualizationHint.Vertical ?  this.props.selectedTag.definition.fields : this.props.selectedTag.fields,
+        selectedTableTagBody: this.props.selectedTableTagBody,
+    };
+
+    public componentDidMount = async () => {
+        if (this.props.selectedTag.type === FieldType.Array) {
+            const rows = [{ fieldKey: "#0", fieldType: FieldType.String, fieldFormat: FieldFormat.NotSpecified }]
+            for (let i = 1; i < this.props.selectedTableTagBody.length; i++) {
+                rows.push({ fieldKey: "#" + i, fieldType: FieldType.String, fieldFormat: FieldFormat.NotSpecified });
+            }
+            this.setState({ rows });
+        }
+    }
+
+    public componentDidUpdate = async (prevProps: Readonly<ITableTagLabelingProps>, prevState: Readonly<ITableTagLabelingState>) => {
+        if (this.props.selectedTableTagBody.length !== prevProps.selectedTableTagBody.length) {
+            const rows = [{ fieldKey: "#0", fieldType: FieldType.String, fieldFormat: FieldFormat.NotSpecified }]
+            for (let i = 1; i < this.props.selectedTableTagBody.length; i++) {
+                rows.push({ fieldKey: "#" + i, fieldType: FieldType.String, fieldFormat: FieldFormat.NotSpecified });
+            }
+            this.setState({ rows });
+        }
+    }
+
+    public render() {
+        return (
+                <div className="table-labeling_container">
+                    <h4 className="mt-2">{strings.tags.regionTableTags.tableLabeling.title}</h4>
+                    <div className="labeling-guideline">
+                        {strings.tags.regionTableTags.tableLabeling.description.title}
+                        <ol>
+                            <li>{strings.tags.regionTableTags.tableLabeling.description.stepOne}</li>
+                            <li>{strings.tags.regionTableTags.tableLabeling.description.stepTwo}</li>
+                        </ol>
+                    </div>
+                    <h5 className="mb-4 table-name">
+                        <span style={{ borderBottom: `4px solid ${this.props.selectedTag.color}` }}>{`${strings.tags.regionTableTags.tableLabeling.tableName}: ${this.props.selectedTag.name}`}</span>
+                    </h5>
+                    { (this.props.selectedTag.type === FieldType.Object && this.props.selectedTag.fields && this.props.selectedTag.definition.fields) || this.props.selectedTag.definition.fields ?
+                    <div className="table-view-container">
+                    <table className="viewed-table">
+                        <tbody>
+                            {this.getTableBody()}
+                        </tbody>
+                    </table>
+                </div>
+                :
+                <div>Missing fields. Please Reconfigure table.</div>
+                    }
+                    {this.props.selectedTag.type === FieldType.Array && <div className="add-row-button_container">
+                        <PrimaryButton
+                            theme={getPrimaryBlueTheme()}
+                            className="add_button ml-6"
+                            autoFocus={true}
+                            onClick={this.addRow}
+                        >
+                            <FontIcon iconName="Add" className="mr-2" />
+                            {strings.tags.regionTableTags.tableLabeling.buttons.addRow}
+                        </PrimaryButton>
+                    </div>}
+                    <div className="buttons-container">
+                        <PrimaryButton
+                            className="button-done"
+                            theme={getPrimaryGreenTheme()}
+                            onClick={() => {
+                                this.props.setTagInputMode(TagInputMode.Basic, null, null)
+                            }}
+                        >{strings.tags.regionTableTags.tableLabeling.buttons.done}
+                        </PrimaryButton>
+                        <DefaultButton
+                            className="button-reconfigure"
+                            theme={getPrimaryGreenTheme()}
+                            onClick={() => { this.props.setTagInputMode(TagInputMode.ConfigureTable) }}
+                        >{strings.tags.regionTableTags.tableLabeling.buttons.reconfigureTable}
+                        </DefaultButton>
+                    </div>
+                </div>
+        )
+    }
+
+    public getTableBody = () => {
+        const table = { rows: this.state.rows, columns: this.state.columns };
+        const selectedTableTagBody = this.props.selectedTableTagBody;
+        const isRowDynamic = this.props.selectedTag.type === FieldType.Array;
+
+        let tableBody = null;
+        if (table.rows && table.rows?.length !== 0 && table.columns.length !== 0) {
+            tableBody = [];
+            const rows = table[TableElements.rows];
+            const columns = table[TableElements.columns];
+            for (let i = 0; i < rows.length + 1; i++) {
+                const tableRow = [];
+                for (let j = 0; j < columns.length + 1; j++) {
+                    if (i === 0 && j !== 0) {
+                        tableRow.push(<th key={j} className={"column_header"}>{columns[j - 1].fieldKey}</th>);
+                    } else if (j === 0 && i !== 0) {
+                        tableRow.push(<th key={j} className={`row_header ${isRowDynamic ? "hidden" : ""}`}>{rows[i - 1].fieldKey}</th>);
+                    } else if (j === 0 && i === 0) {
+                        tableRow.push(<th key={j} className={`empty_header  ${isRowDynamic ? "hidden" : ""}`} />);
+                    } else {
+                        tableRow.push(
+                            <td
+                                className={"table-cell"}
+                                onClick={() => this.handleCellClick(i - 1, j - 1)} key={j}
+                                onMouseEnter={() => this.handleTableCellMouseEnter(selectedTableTagBody[i - 1][j - 1])}
+                                onMouseLeave={() => this.handleTableCellMouseLeave()}
+                            >
+                                {selectedTableTagBody[i - 1][j - 1]?.find((tableRegion) => tableRegion.value === "") && <FontIcon className="pr-1 pl-1" iconName="FieldNotChanged" />}
+                                {selectedTableTagBody[i - 1][j - 1]?.map((tableRegion) => tableRegion.value).join(" ")}
+                            </td>);
+                    }
+                }
+                tableBody.push(<tr key={i}>{tableRow}</tr>);
+            }
+        }
+
+        return tableBody
+    }
+
+    private addRow = () => {
+        this.props.addRowToDynamicTable()
+    };
+
+    private handleCellClick = (iToChange: number, jToChange: number) => {
+        this.props.handleTableCellClick(iToChange, jToChange)
+    }
+    private handleTableCellMouseEnter = (regions: IRegion[]) => {
+        this.props.handleTableCellMouseEnter(regions)
+    }
+    private handleTableCellMouseLeave = () => {
+        this.props.handleTableCellMouseLeave();
+    }
+}
diff --git a/src/react/components/common/tagInput/tagInput.scss b/src/react/components/common/tagInput/tagInput.scss
index cfb2e55bf..4279ed4f6 100644
--- a/src/react/components/common/tagInput/tagInput.scss
+++ b/src/react/components/common/tagInput/tagInput.scss
@@ -8,6 +8,8 @@
     &-input {
         display: flex;
         flex-grow: 1;
+        overflow-y: auto;
+        max-width: 100% !important;
         flex-direction: column;
         user-select: none;
         background: $lighter-1;
@@ -36,6 +38,18 @@
             &-container{
                 overflow-x: visible;
                 overflow-y: auto;
+                // padding: 0 0 0 100px;
+                // margin: 0 0 0 -100px;
+                &::before{
+                    // content: " ";
+                    // display: inline-block;
+                    // position: absolute;
+                    // width: 80px;
+                    // height: 100%;
+                    // left: -80px;
+                    // background: linear-gradient(to right, #00000000 0%,#000000 100%);
+                }
+
             };
         }
 
diff --git a/src/react/components/common/tagInput/tagInput.test.tsx b/src/react/components/common/tagInput/tagInput.test.tsx
index f0ee44d6c..b48288a86 100644
--- a/src/react/components/common/tagInput/tagInput.test.tsx
+++ b/src/react/components/common/tagInput/tagInput.test.tsx
@@ -30,6 +30,16 @@ describe("Tag Input Component", () => {
             labels: [],
             onLabelEnter: jest.fn(),
             onLabelLeave: jest.fn(),
+            tagInputMode: null,
+            selectedTableTagToLabel: null,
+            handleLabelTable: null,
+            addRowToDynamicTable: null,
+            reconfigureTableConfirm: null,
+            handleTableCellClick: null,
+            selectedTableTagBody: null,
+            splitPaneWidth: null,
+            handleTableCellMouseEnter: null,
+            handleTableCellMouseLeave: null
         };
     }
 
diff --git a/src/react/components/common/tagInput/tagInput.tsx b/src/react/components/common/tagInput/tagInput.tsx
index aab010f9c..c7c69e240 100644
--- a/src/react/components/common/tagInput/tagInput.tsx
+++ b/src/react/components/common/tagInput/tagInput.tsx
@@ -2,30 +2,32 @@
 // Licensed under the MIT license.
 
 import {
-    ContextualMenu,
-    ContextualMenuItemType,
-    Customizer,
-    FontIcon,
-    IContextualMenuItem,
-    ICustomizations,
-    Spinner,
-    SpinnerSize
+    ContextualMenu, ContextualMenuItemType, Customizer,
+    FontIcon, IContextualMenuItem, ICustomizations,
+    Spinner, SpinnerSize, ChoiceGroup, IChoiceGroupOption
 } from "@fluentui/react";
+import { strings, interpolate } from "../../../../common/strings";
+import { getDarkTheme, getPrimaryRedTheme } from "../../../../common/themes";
+import { AlignPortal } from "../align/alignPortal";
+import { filterFormat, getNextColor, getTagCategory } from "../../../../common/utils";
+import {
+    IRegion, ITag, ILabel, FieldType, FieldFormat,
+    TagInputMode, FeatureCategory, ITableTag, ITableRegion,
+    ITableConfigItem, ITableKeyField, ITableLabel, TableElements, TableVisualizationHint
+} from "../../../../models/applicationState";
+import { ColorPicker } from "../colorPicker";
+import "./tagInput.scss";
 import debounce from 'lodash/debounce';
-import React, {KeyboardEvent} from "react";
-import {toast} from "react-toastify";
-import {constants} from "../../../../common/constants";
-import {interpolate, strings} from "../../../../common/strings";
-import {getDarkTheme, getPrimaryRedTheme} from "../../../../common/themes";
-import {getNextColor} from "../../../../common/utils";
-import {FeatureCategory, FieldFormat, FieldType, ILabel, IRegion, ITag} from "../../../../models/applicationState";
+import React, { KeyboardEvent } from "react";
+import { constants } from "../../../../common/constants";
 import Confirm from "../../common/confirm/confirm";
-import {AlignPortal} from "../align/alignPortal";
-import {ColorPicker} from "../colorPicker";
 import "../condensedList/condensedList.scss";
 import "./tagInput.scss";
-import TagInputItem, {ITagClickProps, ITagInputItemProps} from "./tagInputItem";
+import TagInputItem, { ITagClickProps, ITagInputItemProps } from "./tagInputItem";
 import TagInputToolbar from "./tagInputToolbar";
+import { toast } from "react-toastify";
+import TableTagConfig from "./tableTagConfig"
+import TableTagLabeling from "./tableTagLabeling";
 // tslint:disable-next-line:no-var-requires
 const tagColors = require("../../common/tagColors.json");
 
@@ -41,6 +43,7 @@ export enum TagOperationMode {
     ColorPicker,
     ContextualMenu,
     Rename,
+    LabelTable,
 }
 
 export interface ITagInputProps {
@@ -52,6 +55,9 @@ export interface ITagInputProps {
     selectedRegions?: IRegion[];
     /** The labels in the canvas */
     labels: ILabel[];
+    encoded?: boolean;
+    /** The tableLabels in the canvas */
+    tableLabels?: ITableLabel[];
     /** The doc current page number */
     pageNumber: number;
     /** Tags that are currently locked for editing experience */
@@ -69,7 +75,7 @@ export interface ITagInputProps {
     /** Function to call when tag is renamed */
     onTagRename?: (oldTag: ITag, newTag: ITag, cancelCallback: () => void) => void;
     /** Function to call when tag is deleted */
-    onTagDeleted?: (tagName: string) => void;
+    onTagDeleted?: (tagName: string, tagType: FieldType, tagFormat: FieldFormat) => void;
     /** Always show tag input box */
     showTagInputBox?: boolean;
     /** Always show tag search box */
@@ -80,6 +86,17 @@ export interface ITagInputProps {
     onLabelLeave: (label: ILabel) => void;
     /** Function to handle tag change */
     onTagChanged?: (oldTag: ITag, newTag: ITag) => void;
+    setTagInputMode?: (tagInputMode: TagInputMode, selectedTableTagToLabel?: ITableTag) => void;
+    tagInputMode: TagInputMode;
+    selectedTableTagToLabel: ITableTag;
+    handleLabelTable: (tagInputMode: TagInputMode, selectedTableTagToLabel) => void;
+    addRowToDynamicTable: () => void;
+    reconfigureTableConfirm: (originalTagName: string, tagName: string, tagType: FieldType.Array | FieldType.Object, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[]) => void;
+    handleTableCellClick: (iTableCellIndex, jTableCellIndex) => void;
+    selectedTableTagBody: ITableRegion[][][];
+    handleTableCellMouseEnter: (regions) => void;
+    handleTableCellMouseLeave: () => void;
+    splitPaneWidth: number;
     onTagDoubleClick?: (label: ILabel) => void;
 }
 
@@ -94,31 +111,6 @@ export interface ITagInputState {
     selectedTag: ITag;
 }
 
-function filterFormat(type: FieldType): FieldFormat[] {
-    switch (type) {
-        case FieldType.String:
-            return [
-                FieldFormat.NotSpecified,
-                FieldFormat.Alphanumeric,
-                FieldFormat.NoWhiteSpaces,
-            ];
-        case FieldType.Number:
-            return [
-                FieldFormat.NotSpecified,
-                FieldFormat.Currency,
-            ];
-        case FieldType.Date:
-            return [
-                FieldFormat.NotSpecified,
-                FieldFormat.DMY,
-                FieldFormat.MDY,
-                FieldFormat.YMD,
-            ];
-        default:
-            return [ FieldFormat.NotSpecified ];
-    }
-}
-
 function isNameEqual(x: string, y: string) {
     return x.trim().toLocaleLowerCase() === y.trim().toLocaleLowerCase();
 }
@@ -172,92 +164,136 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
             scopedSettings: {},
         };
 
-        const {selectedTag, tagOperation} = this.state;
+        const { selectedTag, tagOperation } = this.state;
         const selectedTagRef = selectedTag ? this.tagItemRefs.get(selectedTag.name)?.getTagNameRef() : null;
 
-        return (
-            <div className="tag-input">
-                <div ref={this.headerRef} className="tag-input-header p-2">
-                    <span className="tag-input-title">{strings.tags.title}</span>
-                    <TagInputToolbar
-                        selectedTag={this.state.selectedTag}
-                        onAddTags={() => this.setState({addTags: !this.state.addTags})}
-                        onOnlyCurrentPageTags={() => this.setState({onlyCurrentPageTags: !this.state.onlyCurrentPageTags})}
-                        onShowOriginLabels = {(showOriginLabels: boolean) => this.setState({showOriginLabels})}
-                        onSearchTags={() => this.setState({
-                            searchTags: !this.state.searchTags,
-                            searchQuery: "",
-                        })}
-                        searchingTags={this.state.searchQuery.length > 0}
-                        onRenameTag={this.onRenameTag}
-                        onLockTag={this.onLockTag}
-                        onDelete={this.onDeleteTag}
-                        onReorder={this.onReOrder}
-                    />
-                </div>
-                {this.props.tagsLoaded ?
+        if (this.props.tagInputMode === TagInputMode.ConfigureTable) {
+            return (
+                <div className="tag-input">
+                    <div className="tag-input-header p-2">
+                        <span className="tag-input-title">{strings.tags.title}</span>
+                    </div>
                     <div className="tag-input-body-container">
                         <div className="tag-input-body">
-                            {
-                                this.state.searchTags &&
-                                <div className="tag-input-text-input-row search-input">
-                                    <input
-                                        className="tag-search-box"
-                                        type="text"
-                                        onKeyDown={this.onSearchKeyDown}
-                                        onChange={(e) => this.setState({searchQuery: e.target.value})}
-                                        placeholder="Search tags"
-                                        autoFocus={true}
-                                        onFocus={() => this.setState({selectedTag: null, tagOperation: TagOperationMode.Rename})}
-                                    />
-                                    <FontIcon iconName="Search" />
+                            <TableTagConfig
+                                setTagInputMode={this.props.setTagInputMode}
+                                addTableTag={this.addTableTag}
+                                splitPaneWidth={this.props.splitPaneWidth}
+                                tableTag={this.props.selectedTableTagToLabel}
+                                reconfigureTableConfirm={this.props.reconfigureTableConfirm}
+                                selectedTableBody={this.props.selectedTableTagBody}
+                            />
+                        </div>
+                    </div>
+                </div>
+            )
+        } else if (this.props.tagInputMode === TagInputMode.LabelTable) {
+            return (
+                <div className="tag-input">
+                    <div className="tag-input-header p-2">
+                        <span className="tag-input-title">{strings.tags.title}</span>
+                    </div>
+                    <TableTagLabeling
+                        onTagClick={this.props.onTagClick}
+                        selectedRegions={this.props.selectedRegions}
+                        setTagInputMode={this.props.setTagInputMode}
+                        selectedTag={this.props.selectedTableTagToLabel as ITableTag}
+                        handleTableCellClick={this.props.handleTableCellClick}
+                        handleTableCellMouseEnter={this.props.handleTableCellMouseEnter}
+                        handleTableCellMouseLeave={this.props.handleTableCellMouseLeave}
+                        selectedTableTagBody={this.props.selectedTableTagBody}
+                        splitPaneWidth={this.props.splitPaneWidth}
+                        addRowToDynamicTable={this.props.addRowToDynamicTable}
+                    />
+                </div>
+            )
+        } else {
+            return (
+                <div className="tag-input">
+                    <div ref={this.headerRef} className="tag-input-header p-2">
+                        <span className="tag-input-title">{strings.tags.title}</span>
+                        <TagInputToolbar
+                            selectedTag={this.state.selectedTag}
+                            onAddTags={() => this.setState({ addTags: !this.state.addTags })}
+                            onOnlyCurrentPageTags={() => this.setState({ onlyCurrentPageTags: !this.state.onlyCurrentPageTags })}
+                            onShowOriginLabels={(showOriginLabels: boolean) => this.setState({ showOriginLabels })}
+                            onSearchTags={() => this.setState({
+                                searchTags: !this.state.searchTags,
+                                searchQuery: "",
+                            })}
+                            searchingTags={this.state.searchQuery.length > 0}
+                            onRenameTag={this.onRenameTag}
+                            onLockTag={this.onLockTag}
+                            onDelete={this.onDeleteTag}
+                            onReorder={this.onReOrder}
+                            setTagInputMode={this.props.setTagInputMode}
+                        />
+                    </div>
+                    {this.props.tagsLoaded ?
+                        <div className="tag-input-body-container">
+                            <div className="tag-input-body">
+                                {
+                                    this.state.searchTags &&
+                                    <div className="tag-input-text-input-row search-input">
+                                        <input
+                                            className="tag-search-box"
+                                            type="text"
+                                            onKeyDown={this.onSearchKeyDown}
+                                            onChange={(e) => this.setState({ searchQuery: e.target.value })}
+                                            placeholder="Search tags"
+                                            autoFocus={true}
+                                            onFocus={() => this.setState({ selectedTag: null, tagOperation: TagOperationMode.Rename })}
+                                        />
+                                        <FontIcon iconName="Search" />
+                                    </div>
+                                }
+                                <div className="tag-input-items">
+                                    {this.renderTagItems()}
+                                    <Customizer {...dark}>
+                                        {
+                                            tagOperation === TagOperationMode.ContextualMenu && selectedTagRef &&
+                                            <ContextualMenu
+                                                className="tag-input-contextual-menu"
+                                                items={this.getContextualMenuItems()}
+                                                target={selectedTagRef}
+                                                onDismiss={this.onHideContextualMenu}
+                                            />
+                                        }
+                                    </Customizer>
+                                    {this.getColorPickerPortal()}
                                 </div>
-                            }
-                            <div className="tag-input-items">
-                                {this.renderTagItems()}
-                                <Customizer {...dark}>
-                                    {
-                                        tagOperation === TagOperationMode.ContextualMenu && selectedTagRef &&
-                                        <ContextualMenu
-                                            className="tag-input-contextual-menu"
-                                            items={this.getContextualMenuItems()}
-                                            target={selectedTagRef}
-                                            onDismiss={this.onHideContextualMenu}
+                                {
+                                    this.state.addTags &&
+                                    <div className="tag-input-text-input-row new-tag-input">
+                                        <input
+                                            className="tag-input-box"
+                                            type="text"
+                                            onKeyDown={this.onAddTagKeyDown}
+                                            // Add mouse event
+                                            onBlur={this.onAddTagWithBlur}
+                                            placeholder="Add new tag"
+                                            autoFocus={true}
+                                            ref={this.inputRef}
                                         />
-                                    }
-                                </Customizer>
-                                {this.getColorPickerPortal()}
+                                        <FontIcon iconName="Tag" />
+                                    </div>
+                                }
                             </div>
-                            {
-                                this.state.addTags &&
-                                <div className="tag-input-text-input-row new-tag-input">
-                                    <input
-                                        className="tag-input-box"
-                                        type="text"
-                                        onKeyDown={this.onAddTagKeyDown}
-                                        // Add mouse event
-                                        onBlur={this.onAddTagWithBlur}
-                                        placeholder="Add new tag"
-                                        autoFocus={true}
-                                        ref={this.inputRef}
-                                    />
-                                    <FontIcon iconName="Tag" />
-                                </div>
-                            }
                         </div>
-                    </div>
-                    :
-                    <Spinner className="loading-tag" size={SpinnerSize.large} />
-                }
-                <Confirm
-                    title={strings.tags.warnings.replaceAllExitingLabelsTitle}
-                    ref={this.replaceConfirmRef}
-                    message={strings.tags.warnings.replaceAllExitingLabels}
-                    confirmButtonTheme={getPrimaryRedTheme()}
-                    onConfirm={this.onReplaceConfirm}
-                />
-            </div>
-        );
+
+                        :
+                        <Spinner className="loading-tag" size={SpinnerSize.large} />
+                    }
+                    <Confirm
+                        title={strings.tags.warnings.replaceAllExitingLabelsTitle}
+                        ref={this.replaceConfirmRef}
+                        message={strings.tags.warnings.replaceAllExitingLabels}
+                        confirmButtonTheme={getPrimaryRedTheme()}
+                        onConfirm={this.onReplaceConfirm}
+                    />
+                </div>
+            );
+        }
     }
     public triggerNewTagBlur() {
         if (this.inputRef.current) {
@@ -372,14 +408,14 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
         if (!tag) {
             return;
         }
-        this.props.onTagDeleted(tag.name);
+        this.props.onTagDeleted(tag.name, tag.type, tag.format);
     }
 
     private getColorPickerPortal = () => {
         const {selectedTag} = this.state;
         const showColorPicker = this.state.tagOperation === TagOperationMode.ColorPicker;
         return (
-            <AlignPortal align={{points: [ "tr", "tl" ]}} target={() => this.headerRef.current}>
+            <AlignPortal align={{ points: ["tr", "tl"] }} target={() => this.headerRef.current}>
                 <div className="tag-input-colorpicker-container">
                     {
                         showColorPicker &&
@@ -414,6 +450,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
                 onLabelEnter={this.props.onLabelEnter}
                 onLabelLeave={this.props.onLabelLeave}
                 onTagChanged={this.props.onTagChanged}
+                handleLabelTable={this.props.handleLabelTable}
                 onTagDoubleClick={this.props.onTagDoubleClick}
             />);
     }
@@ -423,21 +460,29 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
         return item;
     }
 
-    private setTagLabels = (key: string): ILabel[] => {
-        return this.props.labels.filter((label) => label.label === key);
+    private setTagLabels = (key: string): any[] => {
+        const labels = this.props.labels.filter((label) => {
+            if (this.props.encoded) {
+                return label.label.replace(/\~1/g, "/").replace(/\~0/g, "~") === key;
+            } else {
+                return label.label === key;
+            }
+        })
+        const tableLables = this.props.tableLabels.filter((label) => label.tableKey === key);
+        return [...labels, ...tableLables]
     }
 
     private createTagItemProps = (): ITagInputItemProps[] => {
-        const {tags, selectedTag, tagOperation, onlyCurrentPageTags} = this.state;
+        const { tags, selectedTag, tagOperation, onlyCurrentPageTags } = this.state;
         const selectedRegionTagSet = this.getSelectedRegionTagSet();
 
         if (onlyCurrentPageTags) {
+            const labels = this.props.labels.filter(item => item.value[0]?.page === this.props.pageNumber).map(item => item.label);
+            const tableLabels = this.props.tableLabels.filter(item => item.labels[0]?.value[0]?.page === this.props.pageNumber).map(item => item.tableKey);
+            const labeledTags = [...labels, ...tableLabels];
 
-            const labels = this.props.labels.filter(item => item.value[ 0 ]?.page === this.props.pageNumber)
-                .map(item => item.label);
-            if (labels.length) {
-
-                return tags.filter(tag => labels.find(a => a === tag.name))
+            if (labeledTags.length) {
+                return tags.filter(tag => labeledTags.find(a => a === tag.name))
                     .map<ITagInputItemProps>(tag => {
                         return {
                             tag,
@@ -518,19 +563,22 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
             // Only fire click event if a region is selected
             const {selectedRegions, onTagClick, labels} = this.props;
             if (selectedRegions && selectedRegions.length && onTagClick) {
-                const {category} = selectedRegions[ 0 ];
-                const {format, type, documentCount, name} = tag;
-                const tagCategory = this.getTagCategory(type);
+                const { category } = selectedRegions[0];
+                const { format, type, documentCount, name } = tag;
+                const tagCategory = getTagCategory(type);
                 const isTagLabelTypeDrawnRegion = this.labelAssignedDrawnRegion(labels, tag.name);
                 const labelAssigned = this.labelAssigned(labels, name);
 
-                if (labelAssigned && ((category === FeatureCategory.DrawnRegion) !== isTagLabelTypeDrawnRegion)) {
-                    if(category===FeatureCategory.Checkbox&&isTagLabelTypeDrawnRegion){
-                        toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, {otherCatagory: FeatureCategory.Checkbox}));
-                    }else if (isTagLabelTypeDrawnRegion) {
+                if ((tag.type === FieldType.Object || tag.type === FieldType.Array) && this.props.selectedRegions?.length) {
+                    this.props.handleLabelTable(TagInputMode.LabelTable, tag)
+                    deselect = false;
+                } else if (labelAssigned && ((category === FeatureCategory.DrawnRegion) !== isTagLabelTypeDrawnRegion)) {
+                    if (category === FeatureCategory.Checkbox && isTagLabelTypeDrawnRegion) {
+                        toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCatagory: FeatureCategory.Checkbox }));
+                    } else if (isTagLabelTypeDrawnRegion) {
                         this.replaceConfirmRef.current.open(tag, props);
                     } else if (tagCategory === FeatureCategory.Checkbox) {
-                        toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, {otherCatagory: FeatureCategory.Checkbox}));
+                        toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCatagory: FeatureCategory.Checkbox }));
                     } else {
                         this.replaceConfirmRef.current.open(tag, props);
                     }
@@ -541,14 +589,14 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
                         toast.warn(strings.tags.warnings.checkboxPerTagLimit);
                         return;
                     }
-                    if(tagCategory===FeatureCategory.Checkbox&&category!==FeatureCategory.Checkbox){
+                    if (tagCategory === FeatureCategory.Checkbox && category !== FeatureCategory.Checkbox) {
                         toast.warn(strings.tags.warnings.notCompatibleTagType);
                         return;
                     }
                     onTagClick(tag);
                     deselect = false;
                 } else {
-                    toast.warn(strings.tags.warnings.notCompatibleTagType, {autoClose: 7000});
+                    toast.warn(strings.tags.warnings.notCompatibleTagType, { autoClose: 7000 });
                 }
             }
             this.setState({
@@ -597,16 +645,6 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
         }
     }
 
-    public getTagCategory = (tagType: string) => {
-        switch (tagType) {
-            case FieldType.SelectionMark:
-            case "checkbox":
-                return "checkbox";
-            default:
-                return "text";
-        }
-    }
-
     private onSearchKeyDown = (event: KeyboardEvent): void => {
         if (event.key === "Escape") {
             this.setState({
@@ -653,6 +691,21 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
         }
     }
 
+    private addTableTag = (tableConfig: any) => {
+        const newTag: ITableTag = {
+            name: tableConfig.name,
+            color: getNextColor(this.state.tags),
+            type: tableConfig.type,
+            format: tableConfig.format,
+            documentCount: 0,
+            itemType: tableConfig.itemType,
+            fields: tableConfig.fields,
+            definition: tableConfig.definition,
+            visualizationHint: tableConfig.visualizationHint,
+        };
+        this.addTag(newTag);
+    }
+
     private validateTagLength = (tag: ITag) => {
         if (!tag.name.trim().length) {
             throw new Error(strings.tags.warnings.emptyName);
@@ -669,7 +722,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
     }
 
     private onHideContextualMenu = () => {
-        this.setState({tagOperation: TagOperationMode.None});
+        this.setState({ tagOperation: TagOperationMode.None });
     }
 
     private getContextualMenuItems = (): IContextualMenuItem[] => {
@@ -686,8 +739,11 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
                 },
                 text: tag.type ? tag.type : strings.tags.toolbar.type,
                 subMenuProps: {
-                    items: this.getTypeSubMenuItems(),
+                    items: this.getTypeSubMenuItems()
                 },
+                submenuIconProps: {
+                    iconName: tag.type !== FieldType.Object ? "ChevronRight" : ""
+                }
             },
             {
                 key: "format",
@@ -698,6 +754,18 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
                 subMenuProps: {
                     items: this.getFormatSubMenuItems(),
                 },
+                submenuIconProps: {
+                    iconName: tag.type !== FieldType.Object && tag.type !== FieldType.Array ? "ChevronRight" : ""
+                }
+
+            },
+            {
+                key: "reconfigureTable",
+                iconProps: {
+                    iconName: "EditTable",
+                },
+                text: strings.tags.regionTableTags.tableLabeling.buttons.reconfigureTable,
+                onClick: () => this.props.setTagInputMode(TagInputMode.ConfigureTable, tag as ITableTag),
             },
             {
                 key: "divider_1",
@@ -738,23 +806,28 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
                 onClick: this.onMenuItemClick,
             },
         ];
-
-        return menuItems;
+        return tag.type === FieldType.Object || tag.type === FieldType.Array ? menuItems : menuItems.filter((item) => (item.key !== "reconfigureTable"));
+        // return menuItems;
     }
 
     private isTypeCompatibleWithTag = (tag, type) => {
         // If free tag we can assign any type
-        if (tag && tag.documentCount <= 0) {
+        if (tag && tag.documentCount <= 0 && tag.type !== FieldType.Object && tag.type !== FieldType.Array) {
             return true;
         }
-        const tagType = this.getTagCategory(tag.type);
-        const menuItemType = this.getTagCategory(type);
+        const tagType = getTagCategory(tag.type);
+        const menuItemType = getTagCategory(type);
         return tagType === menuItemType;
     }
-
+    // here
     private getTypeSubMenuItems = (): IContextualMenuItem[] => {
         const tag = this.state.selectedTag;
-        const types = Object.values(FieldType);
+        let types = Object.values(FieldType);
+        if (tag.type === FieldType.Object || tag.type === FieldType.Array) {
+            return []
+        } else {
+            types = types.filter((i) => i !== FieldType.Array && i !== FieldType.Object)
+        }
         return types.map((type) => {
             const isCompatible = this.isTypeCompatibleWithTag(tag, type);
             return {
@@ -771,6 +844,9 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
     private getFormatSubMenuItems = (): IContextualMenuItem[] => {
         const tag = this.state.selectedTag;
         const formats = filterFormat(tag.type);
+        if (tag.type === FieldType.Object || tag.type === FieldType.Array) {
+            return []
+        }
 
         return formats.map((format) => {
             return {
diff --git a/src/react/components/common/tagInput/tagInputItem.test.tsx b/src/react/components/common/tagInput/tagInputItem.test.tsx
index 7dc776700..7c2722ad2 100644
--- a/src/react/components/common/tagInput/tagInputItem.test.tsx
+++ b/src/react/components/common/tagInput/tagInputItem.test.tsx
@@ -21,6 +21,8 @@ describe("Tag Input Item", () => {
             onRename: jest.fn(),
             onLabelEnter: jest.fn(),
             onLabelLeave: jest.fn(),
+            handleLabelTable: null,
+            addRowToDynamicTable: null,
             showOriginLabels:false,
         };
     }
diff --git a/src/react/components/common/tagInput/tagInputItem.tsx b/src/react/components/common/tagInput/tagInputItem.tsx
index 20f05b240..1c32950b5 100644
--- a/src/react/components/common/tagInput/tagInputItem.tsx
+++ b/src/react/components/common/tagInput/tagInputItem.tsx
@@ -1,12 +1,12 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import {FontIcon, IconButton} from "@fluentui/react";
+import { FontIcon, IconButton } from "@fluentui/react";
 import _ from "lodash";
-import React, {Fragment, MouseEvent} from "react";
-import {strings} from "../../../../common/strings";
-import {FieldFormat, FieldType, ILabel, ITag} from "../../../../models/applicationState";
-import {tagIndexKeys} from "./tagIndexKeys";
+import React, { Fragment, MouseEvent } from "react";
+import { strings } from "../../../../common/strings";
+import { FieldFormat, FieldType, ILabel, ITableLabel, ITag, TagInputMode } from "../../../../models/applicationState";
+import { tagIndexKeys } from "./tagIndexKeys";
 import TagInputItemLabel from "./tagInputItemLabel";
 
 export interface ITagClickProps {
@@ -41,9 +41,11 @@ export interface ITagInputItemProps {
     onClick: (tag: ITag, props: ITagClickProps) => void;
     /** Apply new name to tag */
     onRename: (oldTag: ITag, newName: string, cancelCallback: () => void) => void;
-    onLabelEnter: (label: ILabel) => void;
+    onLabelEnter: (label: ILabel|ITableLabel) => void;
     onLabelLeave: (label: ILabel) => void;
     onTagChanged?: (oldTag: ITag, newTag: ITag) => void;
+    handleLabelTable: (tagInputMode: TagInputMode, selectedTableTagToLabel) => void;
+    addRowToDynamicTable: () => void;
     onTagDoubleClick?: (label: ILabel) => void;
 }
 
@@ -185,20 +187,23 @@ export default class TagInputItem extends React.Component<ITagInputItemProps, IT
                     <FontIcon iconName="Link" className="pl-1" />
                 }
                 <div className="tag-name-body">
-                    <input
-                        ref={this.onInputRef}
-                        style={{display: this.state.isRenaming ? "block" : "none"}}
-                        className={`tag-name-editor ${this.getContentClassName()}`}
-                        type="text"
-                        defaultValue={this.props.tag.name}
-                        onKeyDown={(e) => this.onInputKeyDown(e)}
-                        onBlur={this.onInputBlur}
-                        autoFocus={true}
-                    />
-
-                    {!this.state.isRenaming && <span title={spanValue} className={this.getContentClassName()}>
-                        {spanValue}
-                    </span>}
+                    {
+                        this.state.isRenaming
+                            ?
+                            <input
+                                ref={this.onInputRef}
+                                className={`tag-name-editor ${this.getContentClassName()}`}
+                                type="text"
+                                defaultValue={this.props.tag.name}
+                                onKeyDown={(e) => this.onInputKeyDown(e)}
+                                onBlur={this.onInputBlur}
+                                autoFocus={true}
+                            />
+                            :
+                            <span title={this.props.tag.name} className={this.getContentClassName()}>
+                                {this.props.tag.name}
+                            </span>
+                    }
                 </div>
                 <div className={"tag-icons-container"}>
                     {(displayIndex !== null)
@@ -211,7 +216,7 @@ export default class TagInputItem extends React.Component<ITagInputItemProps, IT
                         title={strings.tags.toolbar.contextualMenu}
                         ariaLabel={strings.tags.toolbar.contextualMenu}
                         className="tag-input-toolbar-iconbutton ml-2"
-                        iconProps={{iconName: "ChevronDown"}}
+                        iconProps={{ iconName: "ChevronDown" }}
                         onClick={this.onDropdownClick} />
                 </div>
             </div>
@@ -219,46 +224,65 @@ export default class TagInputItem extends React.Component<ITagInputItemProps, IT
     }
 
     private renderTagDetail = () => {
-        let confidence = _.get(this.props, "labels[0].confidence", null);
-        if (confidence > .995) {
-            confidence = 0.995;
-        }
-        const revised = _.get(this.props, "labels[0].revised", false);
-        return this.props.labels.map((label, idx) =>
-            <Fragment key={idx}>
-                <div className="tag-item-label-container">
-                    {(confidence||revised) &&
-                        <div className="tag-item-label-container-item1">
-                            {!revised && confidence &&
-                                <div className="tag-item-confidence">
-                                    {confidence}
-                                </div>
+        if (this.props.tag.type === FieldType.Object || this.props.tag.type === FieldType.Array) {
+            return (
+                <div
+                    className={"tag-item-label px-2"}
+                    onClick={() => {
+                        this.props.handleLabelTable(TagInputMode.LabelTable, this.props.tag);
+                        this.props.onLabelLeave(this.props.labels[0]);
+                    }}
+                    onMouseEnter={() => this.props.onLabelEnter(this.props.labels[0])}
+                    onMouseLeave={() => this.props.onLabelLeave(this.props.labels[0])}
+                >
+                    <FontIcon
+                        className="pr-1 pl-1" iconName="Table"
+                    />
+                Click to assign labels
+                </div>
+            );
+        } else {
+            let confidence = _.get(this.props, "labels[0].confidence", null);
+            if (confidence > .995) {
+                confidence = 0.995;
+            }
+            const revised = _.get(this.props, "labels[0].revised", false);
+            return this.props.labels.map((label, idx) =>
+                <Fragment key={idx}>
+                    <div className="tag-item-label-container">
+                        {(confidence || revised) &&
+                            <div className="tag-item-label-container-item1">
+                                {!revised && confidence &&
+                                    <div className="tag-item-confidence">
+                                        {confidence}
+                                    </div>
+                                }
+                                {revised &&
+                                    <FontIcon iconName="StatusCircleCheckmark" className="ms-Icon-25px" />
+                                }
+                            </div>
+                        }
+                        <div className="tag-item-label-container-item2">
+                            {this.props.showOriginLabels && label.originValue &&
+                                <TagInputItemLabel
+                                    label={label}
+                                    isOrigin={true}
+                                    value={label.originValue}
+                                    prefixText={strings.tags.preText.autoLabel}/>
                             }
-                            {revised &&
-                                <FontIcon iconName="StatusCircleCheckmark" className="ms-Icon-25px" />
+                            {(label.originValue?.length > 0 || label.value?.length > 0) &&
+                                <TagInputItemLabel
+                                    label={label}
+                                    value={label.value}
+                                    isOrigin={false}
+                                    onLabelEnter={this.props.onLabelEnter}
+                                    onLabelLeave={this.props.onLabelLeave}
+                                    prefixText={revised ? strings.tags.preText.revised : undefined}/>
                             }
                         </div>
-                    }
-                    <div className="tag-item-label-container-item2">
-                        {this.props.showOriginLabels && label.originValue &&
-                            <TagInputItemLabel
-                                label={label}
-                                isOrigin={true}
-                                value={label.originValue}
-                                prefixText={strings.tags.preText.autoLabel}
-                            />
-                        }
-                        {(label.originValue?.length > 0 || label.value?.length > 0) && <TagInputItemLabel
-                            label={label}
-                            value={label.value}
-                            isOrigin={false}
-                            onLabelEnter={this.props.onLabelEnter}
-                            onLabelLeave={this.props.onLabelLeave}
-                            prefixText={revised ? strings.tags.preText.revised : undefined}
-                        />}
                     </div>
-                </div>
-            </Fragment>);
+                </Fragment>);
+        }
     }
     private onInputRef = (element: HTMLInputElement) => {
         this.inputElement = element;
@@ -312,7 +336,7 @@ export default class TagInputItem extends React.Component<ITagInputItemProps, IT
     }
 
     private isTypeOrFormatSpecified = () => {
-        const {tag} = this.props;
+        const { tag } = this.props;
         return (tag.type && tag.type !== FieldType.String) ||
             (tag.format && tag.format !== FieldFormat.NotSpecified);
     }
diff --git a/src/react/components/common/tagInput/tagInputItemLabel.tsx b/src/react/components/common/tagInput/tagInputItemLabel.tsx
index ba48406db..4140774f8 100644
--- a/src/react/components/common/tagInput/tagInputItemLabel.tsx
+++ b/src/react/components/common/tagInput/tagInputItemLabel.tsx
@@ -2,11 +2,12 @@
 // Licensed under the MIT license.
 
 import React from "react";
-import {ILabel, IFormRegion} from "../../../../models/applicationState";
-import {FontIcon} from "@fluentui/react";
+import { ILabel, IFormRegion, ITag } from "../../../../models/applicationState";
+import { FontIcon } from "@fluentui/react";
 
 export interface ITagInputItemLabelProps {
     label: ILabel;
+    tag?: ITag;
     value: IFormRegion[];
     isOrigin: boolean;
     onLabelEnter?: (label: ILabel) => void;
@@ -14,28 +15,40 @@ export interface ITagInputItemLabelProps {
     prefixText?:string
 }
 
-export interface ITagInputItemLabelState {}
+export interface ITagInputItemLabelState { }
 
-export default class TagInputItemLabel extends React.Component<ITagInputItemLabelProps, ITagInputItemLabelState> {
-    public render() {
-        const texts = [];
-        let hasEmptyTextValue = false;
-        this.props.value?.forEach((formRegion: IFormRegion, idx) => {
-            if (formRegion.text === "") {
-                hasEmptyTextValue = true;
-            } else {
-                texts.push(formRegion.text);
-            }
-        })
-        const text = texts.join(" ");
+export default function TagInputItemLabel(props: ITagInputItemLabelProps) {
+    const { label, onLabelEnter, onLabelLeave, tag = null , value} = props
+    const texts = [];
+    let hasEmptyTextValue = false;
+    value?.forEach((formRegion: IFormRegion, idx) => {
+        if (formRegion.text === "") {
+            hasEmptyTextValue = true;
+        } else {
+            texts.push(formRegion.text);
+        }
+    })
+    const text = texts.join(" ");
+
+    const handleMouseEnter = () => {
+        if (props.onLabelEnter) {
+            onLabelEnter(label);
+        }
+    };
+
+    const handleMouseLeave = () => {
+        if (props.onLabelLeave) {
+            onLabelLeave(label);
+        }
+    };
         return (
             <div
-                className={[this.props.isOrigin ? "tag-item-label-origin" : "tag-item-label", "flex-center", "px-2"].join(" ")}
-                onMouseEnter={this.handleMouseEnter}
-                onMouseLeave={this.handleMouseLeave}
+                onMouseEnter={handleMouseEnter}
+                onMouseLeave={handleMouseLeave}
+                className={[props.isOrigin ? "tag-item-label-origin" : "tag-item-label", "flex-center", "px-2"].join(" ")}
             >
                 <div className="flex-center">
-                    {text ? this.props.prefixText : undefined} {text}
+                    {text ? props.prefixText : undefined} {text}
                     {hasEmptyTextValue &&
                         <FontIcon className="pr-1 pl-1" iconName="FieldNotChanged" />
                     }
@@ -43,16 +56,3 @@ export default class TagInputItemLabel extends React.Component<ITagInputItemLabe
             </div>
         );
     }
-
-    private handleMouseEnter = () => {
-        if (this.props.onLabelEnter) {
-            this.props.onLabelEnter(this.props.label);
-        }
-    }
-
-    private handleMouseLeave = () => {
-        if (this.props.onLabelLeave) {
-            this.props.onLabelLeave(this.props.label);
-        }
-    }
-}
diff --git a/src/react/components/common/tagInput/tagInputToolbar.tsx b/src/react/components/common/tagInput/tagInputToolbar.tsx
index c034db337..3bf30e6c4 100644
--- a/src/react/components/common/tagInput/tagInputToolbar.tsx
+++ b/src/react/components/common/tagInput/tagInputToolbar.tsx
@@ -2,9 +2,9 @@
 // Licensed under the MIT license.
 
 import React from "react";
-import {IconButton} from "@fluentui/react";
-import {strings} from "../../../../common/strings";
-import {ITag} from "../../../../models/applicationState";
+import { IconButton } from "@fluentui/react";
+import { strings } from "../../../../common/strings";
+import { ITableRegion, ITableTag, ITag, TagInputMode } from "../../../../models/applicationState";
 import {constants} from "../../../../common/constants";
 
 enum Categories {
@@ -20,6 +20,8 @@ export interface ITagInputToolbarProps {
     selectedTag: ITag;
     /** Function to call when add tags button is clicked */
     onAddTags: () => void;
+
+    setTagInputMode?: (tagInputMode: TagInputMode, selectedTableTagToLabel?: ITableTag, selectedTableTagBody?: ITableRegion[][][]) => void;
     /** Function to call when search tags button is clicked */
     onSearchTags: () => void;
     /** Function to call when lock tags button is clicked */
@@ -70,6 +72,16 @@ export default class TagInputToolbar extends React.Component<ITagInputToolbarPro
                 category: Categories.General,
                 handler: this.handleAdd,
             },
+            {
+                displayName: strings.tags.toolbar.addTable,
+                icon: "AddTable",
+                category: Categories.General,
+                handler: this.handleAddTable,
+            },
+            {
+                displayName: strings.tags.toolbar.vertiline,
+                category: Categories.Separator,
+            },
             {
                 displayName: this.state.tagFilterToggled ? strings.tags.toolbar.showAllTags : strings.tags.toolbar.onlyShowCurrentPageTags,
                 icon: this.state.tagFilterToggled ? "ClearFilter" : "Filter",
@@ -200,6 +212,10 @@ export default class TagInputToolbar extends React.Component<ITagInputToolbarPro
         });
     }
 
+    private handleAddTable = () => {
+        this.props.setTagInputMode(TagInputMode.ConfigureTable, null, null);
+    }
+
     private handleSearch = () => {
         this.props.onSearchTags();
     }
diff --git a/src/react/components/pages/editorPage/canvas.scss b/src/react/components/pages/editorPage/canvas.scss
index ffd4e1ba5..adc0e25eb 100644
--- a/src/react/components/pages/editorPage/canvas.scss
+++ b/src/react/components/pages/editorPage/canvas.scss
@@ -40,14 +40,14 @@
 .prev {
   position: absolute;
   top: 50%;
-  left: 0;
+  left: 50px;
   margin-left: 10px;
 }
 
 .next {
   position: absolute;
   top: 50%;
-  right: 0;
+  right: 50px;
   margin-right: 10px;
 }
 
diff --git a/src/react/components/pages/editorPage/canvas.test.tsx b/src/react/components/pages/editorPage/canvas.test.tsx
index a1a85af44..02b08f9f0 100644
--- a/src/react/components/pages/editorPage/canvas.test.tsx
+++ b/src/react/components/pages/editorPage/canvas.test.tsx
@@ -43,6 +43,7 @@ describe("Editor Canvas", () => {
             lockedTags: [],
             hoveredLabel: null,
             appSettings: null,
+            highlightedTableCell: null,
         };
 
         const assetPreviewProps: IAssetPreviewProps = {
diff --git a/src/react/components/pages/editorPage/canvas.tsx b/src/react/components/pages/editorPage/canvas.tsx
index afa352b85..7006a741f 100644
--- a/src/react/components/pages/editorPage/canvas.tsx
+++ b/src/react/components/pages/editorPage/canvas.tsx
@@ -9,7 +9,7 @@ import {
     EditorMode, IAssetMetadata,
     IProject, IRegion, RegionType,
     AssetType, ILabelData, ILabel,
-    ITag, IAsset, IFormRegion, FeatureCategory, FieldType, FieldFormat, LabelType, AssetLabelingState, APIVersionPatches, AssetState
+    ITag, IAsset, IFormRegion, FeatureCategory, FieldType, FieldFormat, ImageMapParent, LabelType, ITableRegion, ITableTag, ITableLabel, ITableCellLabel, AssetLabelingState, APIVersionPatches, TableVisualizationHint, AssetState
 } from "../../../../models/applicationState";
 import CanvasHelpers from "./canvasHelpers";
 import { AssetPreview } from "../../common/assetPreview/assetPreview";
@@ -51,7 +51,7 @@ export interface ICanvasProps extends React.Props<Canvas> {
     editorMode: EditorMode;
     project: IProject;
     lockedTags: string[];
-    hoveredLabel: ILabel;
+    hoveredLabel: ILabel | any;
     isRunningOCRs?: boolean;
     children?: ReactElement<AssetPreview>;
     setTableToView?: (tableToView: object, tableToViewId: string) => void;
@@ -64,9 +64,11 @@ export interface ICanvasProps extends React.Props<Canvas> {
     onRunningAutoLabelingStatusChanged?: (isRunning: boolean) => void;
     onTagChanged?: (oldTag: ITag, newTag: ITag) => void;
     runOcrForAllDocs?: (runForAllDocs: boolean) => void;
+    handleLabelTable?: () => void;
     runAutoLabelingOnNextBatch?: (batchSize: number) => Promise<void>;
     onAssetDeleted?: () => void;
     onPageLoaded?: (pageNumber: number) => void;
+    highlightedTableCell: any;
 }
 
 export interface ICanvasState {
@@ -127,6 +129,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
         project: null,
         lockedTags: [],
         hoveredLabel: null,
+        highlightedTableCell: null,
         appSettings: null,
     };
 
@@ -211,12 +214,14 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                 await this.loadOcr();
                 this.loadLabelData(asset);
             });
-        } else if (this.isLabelDataChanged(this.props, prevProps)
-            || (prevProps.project
-                && this.needUpdateAssetRegionsFromTags(prevProps.project.tags, this.props.project.tags))) {
+        } else if (
+            this.isLabelDataChanged(this.props, prevProps)
+            || this.isTableLabelDataChanged(this.props, prevProps)
+            || (prevProps.project && this.needUpdateAssetRegionsFromTags(prevProps.project.tags, this.props.project.tags))) {
             this.setState({
                 currentAsset: this.props.selectedAsset
             }, () => {
+
                 const newRegions = this.convertLabelDataToRegions(this.props.selectedAsset.labelData);
                 this.updateAssetRegions(newRegions);
                 this.redrawAllFeatures();
@@ -228,12 +233,18 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
             });
         }
 
-        if (this.props.hoveredLabel !== prevProps.hoveredLabel) {
+        if (this.props.hoveredLabel !== prevProps.hoveredLabel || this.props.highlightedTableCell !== prevProps.highlightedTableCell) {
             this.imageMap.getAllLabelFeatures().map(this.updateHighlightStatus);
             this.imageMap.getAllDrawnLabelFeatures().map(this.updateHighlightStatus);
         }
     }
 
+    public temp = () => {
+        const newRegions = this.convertLabelDataToRegions(this.props.selectedAsset.labelData);
+        this.updateAssetRegions(newRegions);
+        this.redrawAllFeatures();
+    }
+
     public render = () => {
         const hostStyles: Partial<ITooltipHostStyles> = {
             root: {
@@ -245,6 +256,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                 display: this.state.tableIconTooltip.display,
             },
         };
+
         return (
             <div style={{ width: "100%", height: "100%" }}>
                 <KeyboardBinding
@@ -399,10 +411,10 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
             const result = await predictService.getPrediction(assetPath);
             const assetService = new AssetService(this.props.project);
             const assetMetadata = assetService.getAssetPredictMetadata(asset, result);
-            if(assetMetadata) {
+            if (assetMetadata) {
                 await this.props.onAssetMetadataChanged(assetMetadata);
             }
-        } catch(err){
+        } catch (err) {
             this.setState({
                 isError: true,
                 errorTitle: err.title,
@@ -442,8 +454,8 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
      * Toggles tag on all selected regions
      * @param selectedTag Tag name
      */
-    public applyTag = (tag: string) => {
-        const selectedRegions = this.getSelectedRegions();
+    public applyTag = (tag: string, rowIndex?: number, columnIndex?: number) => {
+        const selectedRegions: IRegion[] = this.getSelectedRegions();
         const regionsEmpty = !selectedRegions || !selectedRegions.length;
         if (!tag || regionsEmpty) {
             return;
@@ -453,16 +465,47 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
             return;
         }
         let regions: IRegion[] = [];
+        const inputTag: ITag[] = this.props.project.tags.filter((t) => t.name === tag);
         if (selectedRegions.length > 0) {
             const labelsData = this.state.currentAsset.labelData;
             if (labelsData) {
-                const relatedLabel = labelsData.labels.find((label) => label.label === tag);
+                let relatedLabel;
+                if (inputTag[0].type === FieldType.Array || inputTag[0].type === FieldType.Object) {
+                    let rowKey;
+                    let columnKey;
+                    if (inputTag[0].type === FieldType.Array) {
+                        rowKey = rowIndex.toString();
+                        columnKey = (inputTag as ITableTag[])[0].definition.fields[columnIndex].fieldKey;
+                        relatedLabel = labelsData.labels.find((label) => label.label === (this.encodeLabelString(tag) + "/" + this.encodeLabelString(rowKey) + "/" + this.encodeLabelString(columnKey)));
+                    } else {
+                        if ((inputTag as ITableTag[])[0].visualizationHint === TableVisualizationHint.Vertical) {
+                            rowKey = (inputTag as ITableTag[])[0].fields[rowIndex].fieldKey;
+                            columnKey = (inputTag as ITableTag[])[0].definition.fields[columnIndex].fieldKey;
+                            relatedLabel = labelsData.labels.find((label) => label.label === (this.encodeLabelString(tag) + "/" + this.encodeLabelString(rowKey) + "/" + this.encodeLabelString(columnKey)));
+                        } else {
+                            rowKey = (inputTag as ITableTag[])[0].definition.fields[rowIndex].fieldKey;
+                            columnKey = (inputTag as ITableTag[])[0].fields[columnIndex].fieldKey;
+                            relatedLabel = labelsData.labels.find((label) => label.label === (this.encodeLabelString(tag) + "/" + this.encodeLabelString(columnKey) + "/" + this.encodeLabelString(rowKey)));
+                        }
+                    }
+                } else {
+                    if (labelsData.$schema === constants.labelsSchema) {
+                        relatedLabel = labelsData.labels.find((label) => label.label === this.encodeLabelString(tag));
+                    } else {
+                        relatedLabel = labelsData.labels.find((label) => label.label === tag);
+                    }
+                }
                 if (relatedLabel &&
                     (((relatedLabel.labelType === null || relatedLabel.labelType === undefined) && (selectedRegions[0].category === FeatureCategory.DrawnRegion))
                         || (relatedLabel.labelType !== null && relatedLabel.labelType !== undefined && relatedLabel.labelType !== selectedRegions[0].category))) {
-                    regions = this.convertLabelToRegion(relatedLabel)
+                    regions = this.convertLabelToRegion(relatedLabel, labelsData?.$schema === constants.labelsSchema);
                     regions.forEach((region) => {
                         region.tags = [];
+                        if (region.isTableRegion) {
+                            delete (region as ITableRegion).isTableRegion;
+                            delete (region as ITableRegion).columnKey;
+                            delete (region as ITableRegion).rowKey;
+                        }
                         const regionIndex = this.state.currentAsset.regions.findIndex(r => r.id === region.id);
                         if (regionIndex !== -1) {
                             this.state.currentAsset.regions.splice(regionIndex, 1, region);
@@ -473,11 +516,29 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
         }
 
         const transformer: (tags: string[], tag: string) => string[] = CanvasHelpers.setSingleTag;
-        const inputTag = this.props.project.tags.filter((t) => t.name === tag);
 
         for (const selectedRegion of selectedRegions) {
             selectedRegion.tags = transformer(selectedRegion.tags, tag);
         }
+
+        if (inputTag[0].type === FieldType.Array || inputTag[0].type === FieldType.Object) {
+            for (const selectedRegion of selectedRegions as ITableRegion[]) {
+                if (inputTag[0].type === FieldType.Array) {
+                    selectedRegion.rowKey = "#" + (rowIndex);
+                    selectedRegion.columnKey = (inputTag as ITableTag[])[0].definition.fields[columnIndex].fieldKey;
+                } else {
+                    if ((inputTag as ITableTag[])[0].visualizationHint === TableVisualizationHint.Vertical) {
+                        selectedRegion.rowKey = (inputTag as ITableTag[])[0].fields[rowIndex].fieldKey;
+                        selectedRegion.columnKey = (inputTag as ITableTag[])[0].definition.fields[columnIndex].fieldKey;
+                    } else {
+                        selectedRegion.rowKey = (inputTag as ITableTag[])[0].definition.fields[rowIndex].fieldKey;
+                        selectedRegion.columnKey = (inputTag as ITableTag[])[0].fields[columnIndex].fieldKey;
+                    }
+                }
+                selectedRegion.isTableRegion = true;
+            }
+        }
+
         this.updateRegions([...selectedRegions, ...regions]);
 
         this.selectedRegionIds = [];
@@ -486,7 +547,11 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
         }
 
         if (selectedRegions.length === 1 && selectedRegions[0].category === FeatureCategory.Checkbox) {
-            this.setTagType(inputTag[0], FieldType.SelectionMark);
+            if (inputTag[0].type === FieldType.Object || inputTag[0].type === FieldType.Array) {
+                // selection mark logic placeholder
+            } else {
+                this.setTagType(inputTag[0], FieldType.SelectionMark);
+            }
         } else if (selectedRegions[0].category === FeatureCategory.DrawnRegion) {
             selectedRegions.forEach((selectedRegion) => {
                 this.imageMap.removeDrawnRegionFeature(this.imageMap.getDrawnRegionFeatureByID(selectedRegion.id));
@@ -702,8 +767,8 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                 currentAsset.labelData.labelingState = this.state.currentAsset.labelData.labelingState;
             }
         }
-        if(currentAsset.labelData?.labelingState!==AssetLabelingState.AutoLabeledAndAdjusted
-            &&(!currentAsset.labelData||currentAsset.labelData.labels?.findIndex(label=>label.value.length>0)<0)){
+        if (currentAsset.labelData?.labelingState !== AssetLabelingState.AutoLabeledAndAdjusted
+            && (!currentAsset.labelData || currentAsset.labelData.labels?.findIndex(label => label.value.length > 0) < 0)) {
             delete currentAsset.labelData?.labelingState;
             delete currentAsset.asset.labelingState;
         }
@@ -768,7 +833,6 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                 updatedRegions.push(update);
             }
         }
-
         updatedRegions.sort(this.compareRegionOrder);
         this.updateAssetRegions(updatedRegions, true);
     }
@@ -1093,14 +1157,26 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
     }
 
     private updateHighlightStatus = (feature: any): void => {
-        if (this.props.hoveredLabel) {
-            const label = this.props.hoveredLabel;
+        if (this.props.hoveredLabel || this.props.highlightedTableCell) {
+            let label = this.props.hoveredLabel
             const id = feature.get("id");
-            if (label.value?.find((region) =>
-                id === this.createRegionIdFromBoundingBox(region.boundingBoxes[0], region.page))) {
+            if (label?.tableKey) {
+                const tableLableValues = [];
+                label.labels.forEach((i: { value: ITableLabel[]; }) => {
+                    i.value.forEach((i: ITableLabel) => tableLableValues.push(i))
+                });
+                label = { label: label.tableKey, value: tableLableValues };
+            }
+
+            if (label?.value?.find((region: { boundingBoxes: number[][]; page: number; }) =>
+                id === this.createRegionIdFromBoundingBox(region.boundingBoxes[0], region.page))
+                || this.props.highlightedTableCell?.find(i => i.id === id)) {
                 this.setFeatureProperty(feature, "highlighted", true);
+            } else {
+                this.setFeatureProperty(feature, "highlighted", false);
             }
-        } else if (feature.get("highlighted")) {
+        }
+        else if (feature.get("highlighted")) {
             this.setFeatureProperty(feature, "highlighted", false);
         }
     }
@@ -1270,7 +1346,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
     }
 
     private loadOcr = async (force?: boolean) => {
-        const asset = {...this.state.currentAsset.asset};
+        const asset = { ...this.state.currentAsset.asset };
 
         if (asset.isRunningOCR) {
             // Skip loading OCR this time since it's running. This will be triggered again once it's finished.
@@ -1280,10 +1356,10 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
             const ocr = await this.ocrService.getRecognizedText(asset.path, asset.name, asset.mimeType, this.setOCRStatus, force);
             if (asset.id === this.state.currentAsset.asset.id) {
                 // since get OCR is async, we only set currentAsset's OCR
-                const newAsset={};
-                if(asset.state===AssetState.NotVisited){
-                    asset.state=AssetState.Visited;
-                    newAsset["currentAsset"]={...this.state.currentAsset, asset};
+                const newAsset = {};
+                if (asset.state === AssetState.NotVisited) {
+                    asset.state = AssetState.Visited;
+                    newAsset["currentAsset"] = { ...this.state.currentAsset, asset };
                 }
                 this.setState({
                     ...newAsset,
@@ -1412,59 +1488,175 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
         });
     }
 
-    private convertLabelDataToRegions = (labelData: ILabelData): IRegion[] => {
-        const regions = [];
+    private getLabelLayers = (label: string) => {
+        return this.decodeLabelLayers(label?.split("/"));
+    }
 
-        if (labelData && labelData.labels) {
-            labelData.labels.forEach((label) => {
-                if (label.value) {
-                    label.value.forEach((formRegion) => {
-                        if (formRegion.boundingBoxes) {
-                            formRegion.boundingBoxes.forEach((boundingBox, boundingBoxIndex) => {
-                                const text = this.getBoundingBoxTextFromRegion(formRegion, boundingBoxIndex);
-                                regions.push(this.createRegion(boundingBox, text, label.label, formRegion.page, label?.labelType));
-                            });
-                        }
-                    });
-                }
-            });
+    private getRegionCellKeys = (layers: string[], tableTag: ITableTag) => {
+        let rowKey;
+        let columnKey;
+        if (tableTag.type === FieldType.Object) {
+            const firstLayerField = tableTag.fields.find((field) => {
+                return field.fieldKey === layers[1];
+            })?.fieldKey
+
+            const secondLayerField = tableTag.definition.fields.find((field) => {
+                return field.fieldKey === layers[2];
+            })?.fieldKey
+            if (!firstLayerField || !secondLayerField) {
+                return;
+            }
+            if (tableTag.visualizationHint === TableVisualizationHint.Vertical) {
+                rowKey = firstLayerField;
+                columnKey = secondLayerField;
+            } else {
+                rowKey = secondLayerField;
+                columnKey = firstLayerField;
+            }
+
+        } else if (tableTag.type === FieldType.Array) {
+            const firstLayerField = layers[1];
+            const secondLayerField = tableTag.definition.fields.find((field) => {
+                return field.fieldKey === layers[2];
+            })?.fieldKey;
+            if (!secondLayerField) {
+                return;
+            }
+            rowKey = "#" + firstLayerField;
+            columnKey = secondLayerField;
+        } else {
+            return;
         }
+        return { rowKey, columnKey }
+    }
+
+    private encodeLabelString = (labelString: string): string => {
+        return labelString.replace(/\~/g, "~0").replace(/\//g, "~1")
+    }
+
+    private decodeLabelString = (labelString: string): string => {
+        return labelString.replace(/\~1/g, "/").replace(/\~0/g, "~")
+    }
+
+    private encodeLabelLayers = (layers: string[]): string[] => {
+        return layers.map((layer) => { return this.encodeLabelString(layer) })
+    }
+
+    private decodeLabelLayers = (layers: string[]): string[] => {
+        return layers.map((layer) => { return this.decodeLabelString(layer) })
+    }
+
+    private convertLabelDataToRegions = (labelData: ILabelData): IRegion[] => {
+        let regions = [];
+        const encodedSchema = labelData?.$schema === constants.labelsSchema;
+
+        labelData?.labels?.forEach((label) => {
+
+            regions = [...regions, ...this.convertLabelToRegion(label, encodedSchema)];
+        });
 
         return regions;
     }
-    private convertLabelToRegion = (label: ILabel): IRegion[] => {
+
+    private convertLabelToRegion = (label: ILabel, encodedSchema: boolean): IRegion[] => {
+        const labelValue = label?.label
+        let layers;
+        if (encodedSchema) {
+            layers = this.getLabelLayers(labelValue);
+        }
         const regions = [];
-        if (label.value) {
-            label.value.forEach((formRegion) => {
-                if (formRegion.boundingBoxes) {
-                    formRegion.boundingBoxes.forEach((boundingBox, boundingBoxIndex) => {
-                        const text = this.getBoundingBoxTextFromRegion(formRegion, boundingBoxIndex);
-                        regions.push(this.createRegion(boundingBox, text, label.label, formRegion.page, label?.labelType));
-                    });
+        if (encodedSchema && layers?.length > 1) {
+            // temp check until nested tables are supported
+            if (layers?.length !== 3) {
+                return;
+            }
+            const labelsTag = this.props.project.tags.find((tag) => {
+                return tag.name === layers[0];
+            })
+            if (labelsTag) {
+                const tableTag = labelsTag as ITableTag;
+                const { rowKey, columnKey } = this.getRegionCellKeys(layers, tableTag);
+                if (!rowKey || !columnKey) {
+                    return
                 }
-            });
+                label.value.forEach((formRegion) => {
+                    if (formRegion.boundingBoxes) {
+                        formRegion.boundingBoxes.forEach((boundingBox, boundingBoxIndex) => {
+                            const text = this.getBoundingBoxTextFromRegion(formRegion, boundingBoxIndex);
+                            const tx = { ...this.createRegion(boundingBox, text, labelsTag.name, formRegion.page, label?.labelType), rowKey, columnKey, isTableRegion: true } as ITableRegion;
+                            regions.push(tx);
+                        });
+                    }
+                });
+            } else {
+                return;
+            }
+        } else {
+            if (label.value) {
+                label.value.forEach((formRegion) => {
+                    if (formRegion.boundingBoxes) {
+                        formRegion.boundingBoxes.forEach((boundingBox, boundingBoxIndex) => {
+                            const text = this.getBoundingBoxTextFromRegion(formRegion, boundingBoxIndex);
+                            if (encodedSchema) {
+                                regions.push(this.createRegion(boundingBox, text, this.decodeLabelString(label.label), formRegion.page, label?.labelType));
+                            } else {
+                                regions.push(this.createRegion(boundingBox, text, label.label, formRegion.page, label?.labelType));
+                            }
+                        });
+                    }
+                });
+            }
         }
         return regions;
     }
 
+    private getTableLabelFromRegion = (tableTag: ITableTag, tableRegion: ITableRegion) => {
+        const columnKey = this.encodeLabelString(tableRegion.columnKey);
+        const rowKey = this.encodeLabelString(tableRegion.rowKey);
+        const tableName = this.encodeLabelString(tableTag.name);
+        if (tableTag.type === FieldType.Array) {
+            return tableName + "/" + rowKey.slice(1) + "/" + columnKey;
+        } else if (tableTag.visualizationHint === TableVisualizationHint.Vertical) {
+            return tableName + "/" + rowKey + "/" + columnKey;
+        } else {
+            return tableName + "/" + columnKey + "/" + rowKey;
+        }
+    }
+
     private convertRegionsToLabelData = (regions: IRegion[], assetName: string) => {
-        const labels = (this.props.selectedAsset
-            && this.props.selectedAsset.labelData
-            && this.props.selectedAsset.labelData.labels
-            && this.props.selectedAsset.labelData.labels.map(label => ({
-                ...label, value: []
-            }))) || [];
+        const labelData: ILabelData = {
+            $schema: constants.labelsSchema,
+            document: decodeURIComponent(assetName).split("/").pop(),
+            labels: [] as ILabel[],
+        };
+
+        const labels = (this.props?.selectedAsset?.labelData?.labels?.map(label => {
+            if (this.props.selectedAsset.labelData.$schema === constants.labelsSchema) {
+                return ({
+                    ...label,
+                    value: []
+                })
+            } else {
+                return ({
+                    ...label,
+                    label: this.encodeLabelString(label.label),
+                    value: []
+                })
+            }
+
+        })) || [];
+
         const selectedRegions = this.getSelectedRegions();
         if (selectedRegions.length > 0) {
             const intersectionResult = _.intersection(selectedRegions, regions);
             if (intersectionResult.length === 0) {
                 const relatedLabels = labels.filter(label => selectedRegions.find(sr => sr.tags.find(t => t === label.label)));
-                relatedLabels?.forEach(relatedLabel=>{
+                relatedLabels?.forEach(relatedLabel => {
                     if (relatedLabel && relatedLabel.confidence) {
                         const originLabel = this.props.selectedAsset!.labelData?.labels?.find(a => a.label === relatedLabel.label);
                         if (originLabel) {
                             relatedLabel.revised = true;
-                            if(!relatedLabel.originValue){
+                            if (!relatedLabel.originValue) {
                                 relatedLabel.originValue = [...originLabel.value];
                             }
                         }
@@ -1478,7 +1670,9 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                 item.value?.findIndex(v => v.boundingBoxes?.findIndex(b =>
                     _.isEqual(b, boundingBox)) >= 0 && v.page === region.pageNumber) >= 0);
         }
+
         regions.sort(this.compareRegionOrder);
+
         regions.forEach((region) => {
             const labelType = this.getLabelType(region.category);
             const boundingBox = region.id.split(",").map(parseFloat);
@@ -1488,12 +1682,24 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                 boundingBoxes: [boundingBox],
             } as IFormRegion;
             region.tags.forEach((tag) => {
-                const label = labels.find(label => label.label === tag);
+                let label;
+                if (region.isTableRegion) {
+                    const tableRegion = region as ITableRegion;
+                    const tableTag = this.props.project.tags.find((projectTag) => tag === projectTag.name) as ITableTag;
+                    if (!tableTag) return
+                    label = labels.find(label => label?.label === this.getTableLabelFromRegion(tableTag, tableRegion));
+                } else {
+                    if (this.props.selectedAsset.labelData.$schema === constants.labelsSchema) {
+                        label = labels.find(label => this.decodeLabelString(label?.label) === tag);
+                    } else {
+                        label = labels.find(label => label?.label === tag);
+                    }
+                }
                 if (label) {
                     const originLabel = this.props.selectedAsset!.labelData?.labels?.find(a => a.label === tag);
                     if (originLabel && label.confidence && region.changed) {
                         label.revised = true;
-                        if(!label.originValue){
+                        if (!label.originValue) {
                             label.originValue = [...originLabel.value];
                         }
                     }
@@ -1507,34 +1713,43 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                             }
                         }
                     }
-                    if (originLabel && region.changed && label.labelType !== labelType) {
+                    if (labelType) {
                         label.labelType = labelType;
+                    } else {
+                        delete label.labelType;
                     }
                     label.value.push(formRegion);
                 } else {
                     let newLabel;
+                    let labelName = this.encodeLabelString(tag);
+                    if (region.isTableRegion) {
+                        const tableRegion = region as ITableRegion;
+                        const tableTag = this.props.project.tags.find((projectTag) => tag === projectTag.name) as ITableTag;
+                        if (!tableTag) return
+                        labelName = this.getTableLabelFromRegion(tableTag, tableRegion);
+                    }
                     if (labelType) {
                         newLabel = {
-                            label: tag,
+                            label: labelName,
                             key: null,
                             labelType,
                             value: [formRegion],
                         } as ILabel;
                     } else {
                         newLabel = {
-                            label: tag,
+                            label: labelName,
                             key: null,
                             value: [formRegion],
                         } as ILabel;
                     }
                     labels.push(newLabel);
                 }
+                labelData.labels = [...labels]
             });
         });
-        const labelData:ILabelData={
-            document: decodeURIComponent(assetName).split("/").pop(),
-            labels: [...labels],
-        }
+        labelData.document = decodeURIComponent(assetName).split("/").pop();
+        labelData.labels = labelData.labels.filter((label) => label.value.length > 0 && !label.revised);
+
         return labelData;
     }
 
@@ -1777,7 +1992,9 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
 
     }
     private compareLabelChanged(newLabels: ILabel[], prevLabels: ILabel[]): boolean {
-        if (newLabels.length > 0) {
+        if (newLabels.length !== prevLabels.length) {
+            return true;
+        } else if (newLabels.length > 0) {
             const newFieldNames = newLabels.map((label) => label.label);
             const prevFieldNames = prevLabels.map((label) => label.label);
             if (_.isEqual(newFieldNames.sort(), prevFieldNames.sort())) {
@@ -1785,7 +2002,32 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
                     const newValue = newLabels.find(label => label.label === name).value?.map(region => region.boundingBoxes).join(",");
                     const prevValue = prevLabels.find(label => label.label === name).value?.map(region => region.boundingBoxes).join(",");
                     if (newValue !== prevValue) {
-                         return true;
+                        return true;
+                    }
+                }
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+    }
+
+    private isTableLabelDataChanged = (newProps: ICanvasProps, prevProps: ICanvasProps): boolean => {
+        const newLabels = _.get(newProps, "selectedAsset.labelData.tableLabels", []) as ITableLabel[];
+        const prevLabels = _.get(prevProps, "selectedAsset.labelData.tableLabels", []) as ITableLabel[];
+
+        if (newLabels.length !== prevLabels.length) {
+            return true;
+        } else if (newLabels.length > 0) {
+            const newFieldNames = newLabels.map((label) => label.tableKey);
+            const prevFieldNames = prevLabels.map((label) => label.tableKey);
+            if (_.isEqual(newFieldNames.sort(), prevFieldNames.sort())) {
+                for (const name of newFieldNames) {
+                    const newValue = newLabels.find(label => label.tableKey === name);
+                    const prevValue = prevLabels.find(label => label.tableKey === name);
+                    if (!_.isEqual(newValue, prevValue)) {
+                        return true;
                     }
                 }
                 return false;
@@ -2395,7 +2637,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
     }
 
     async focusOnLabel(label: ILabel) {
-        const page = label.value[ 0 ]?.page;
+        const page = label.value[0]?.page;
         if (page && this.state.currentPage !== page) {
             await this.goToPage(page);
         }
diff --git a/src/react/components/pages/editorPage/canvasCommandBar.tsx b/src/react/components/pages/editorPage/canvasCommandBar.tsx
index 8c83a6211..998030331 100644
--- a/src/react/components/pages/editorPage/canvasCommandBar.tsx
+++ b/src/react/components/pages/editorPage/canvasCommandBar.tsx
@@ -2,14 +2,15 @@
 // Licensed under the MIT license.
 
 import * as React from "react";
-import {CommandBar, ICommandBarItemProps} from "@fluentui/react/lib/CommandBar";
-import {ICustomizations, Customizer} from "@fluentui/react/lib/Utilities";
-import {getDarkGreyTheme} from "../../../../common/themes";
-import {strings} from '../../../../common/strings';
-import {ContextualMenuItemType} from "@fluentui/react";
-import {IProject, IAssetMetadata, AssetLabelingState} from "../../../../models/applicationState";
+import { CommandBar, ICommandBarItemProps } from "@fluentui/react/lib/CommandBar";
+import { ICustomizations, Customizer } from "@fluentui/react/lib/Utilities";
+import { getDarkGreyTheme } from "../../../../common/themes";
+import { interpolate, strings } from '../../../../common/strings';
+import { ContextualMenuItemType } from "@fluentui/react";
+import { IProject, IAssetMetadata, AssetLabelingState } from "../../../../models/applicationState";
 import _ from "lodash";
 import "./canvasCommandBar.scss";
+import { constants } from "../../../../common/constants";
 
 interface ICanvasCommandBarProps {
     handleZoomIn: () => void;
@@ -24,7 +25,6 @@ interface ICanvasCommandBarProps {
     project?: IProject;
     selectedAsset?: IAssetMetadata;
     handleRotateImage: (degrees: number) => void;
-
     drawRegionMode?: boolean;
     connectionType?: string;
     layers?: any;
@@ -213,8 +213,8 @@ export const CanvasCommandBar: React.FunctionComponent<ICanvasCommandBarProps> =
                     {
                         key: "deleteAsset",
                         text: strings.editorPage.asset.delete.title,
-                        iconProps: {iconName: "Delete"},
-                        onClick: () => {if (props.handleAssetDeleted) props.handleAssetDeleted();},
+                        iconProps: { iconName: "Delete" },
+                        onClick: () => { if (props.handleAssetDeleted) props.handleAssetDeleted(); },
                     }
                 ],
             },
diff --git a/src/react/components/pages/editorPage/editorPage.test.tsx b/src/react/components/pages/editorPage/editorPage.test.tsx
index e9ebbd1e2..b168f13d7 100644
--- a/src/react/components/pages/editorPage/editorPage.test.tsx
+++ b/src/react/components/pages/editorPage/editorPage.test.tsx
@@ -425,7 +425,6 @@ describe("Editor Page Component", () => {
             await waitForSelectedAsset(wrapper);
 
             const tagToDelete = project.tags[project.tags.length - 1];
-            wrapper.find(TagInput).props().onTagDeleted(tagToDelete.name);
 
             // Accept the modal delete warning
             wrapper.update();
diff --git a/src/react/components/pages/editorPage/editorPage.tsx b/src/react/components/pages/editorPage/editorPage.tsx
index ff2c288c6..42b1547b6 100644
--- a/src/react/components/pages/editorPage/editorPage.tsx
+++ b/src/react/components/pages/editorPage/editorPage.tsx
@@ -13,7 +13,7 @@ import { strings, interpolate } from "../../../../common/strings";
 import {
     AssetState, AssetType, EditorMode, FieldType,
     IApplicationState, IAppSettings, IAsset, IAssetMetadata,
-    ILabel, IProject, IRegion, ISize, ITag, FeatureCategory, FieldFormat, AssetLabelingState,
+    ILabel, IProject, IRegion, ISize, ITag, FeatureCategory, TagInputMode, FieldFormat, ITableTag, ITableRegion, AssetLabelingState, ITableConfigItem, TableVisualizationHint
 } from "../../../../models/applicationState";
 import IApplicationActions, * as applicationActions from "../../../../redux/actions/applicationActions";
 import IProjectActions, * as projectActions from "../../../../redux/actions/projectActions";
@@ -31,14 +31,15 @@ import EditorSideBar from "./editorSideBar";
 import Alert from "../../common/alert/alert";
 import Confirm from "../../common/confirm/confirm";
 import { OCRService, OcrStatus } from "../../../../services/ocrService";
-import { throttle } from "../../../../common/utils";
+import { getTagCategory, throttle } from "../../../../common/utils";
 import { constants } from "../../../../common/constants";
 import PreventLeaving from "../../common/preventLeaving/preventLeaving";
 import { Spinner, SpinnerSize } from "@fluentui/react/lib/Spinner";
-import { getPrimaryGreenTheme, getPrimaryRedTheme } from "../../../../common/themes";
+import { getPrimaryBlueTheme, getPrimaryGreenTheme, getPrimaryRedTheme } from "../../../../common/themes";
 import { toast } from "react-toastify";
 import { PredictService } from "../../../../services/predictService";
 import { AssetService } from "../../../../services/assetService";
+import clone from "rfdc";
 
 /**
  * Properties for Editor Page
@@ -96,7 +97,13 @@ export interface IEditorPageState {
     errorMessage?: string;
     tableToView: object;
     tableToViewId: string;
+    tagInputMode: TagInputMode;
+    selectedTableTagToLabel: ITableTag;
+    selectedTableTagBody: ITableRegion[][][];
+    rightSplitPaneWidth?: number;
+    reconfigureTableConfirm?: boolean;
     pageNumber: number;
+    highlightedTableCellRegions: ITableRegion[];
 }
 
 function mapStateToProps(state: IApplicationState) {
@@ -133,7 +140,11 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
         hoveredLabel: null,
         tableToView: null,
         tableToViewId: null,
-        pageNumber: 1
+        tagInputMode: TagInputMode.Basic,
+        selectedTableTagToLabel: null,
+        selectedTableTagBody: [[]],
+        pageNumber: 1,
+        highlightedTableCellRegions: null,
     };
 
     private tagInputRef: RefObject<TagInput>;
@@ -144,7 +155,12 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
     private renameCanceled: () => void;
     private deleteTagConfirm: React.RefObject<Confirm> = React.createRef();
     private deleteDocumentConfirm: React.RefObject<Confirm> = React.createRef();
+    private reconfigTableConfirm: React.RefObject<Confirm> = React.createRef();
+    private replaceConfirmRef = React.createRef<Confirm>();
+
+
     private isUnmount: boolean = false;
+    public initialRightSplitPaneWidth: number;
     private isOCROrAutoLabelingBatchRunning = false;
 
     constructor(props) {
@@ -191,6 +207,9 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
         const labels = (selectedAsset &&
             selectedAsset.labelData &&
             selectedAsset.labelData.labels) || [];
+        const tableLabels = (selectedAsset &&
+            selectedAsset.labelData &&
+            selectedAsset.labelData.tableLabels) || [];
 
         const needRunOCRButton = assets.some((asset) => asset.state === AssetState.NotVisited);
 
@@ -198,6 +217,9 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
             return (<div>Loading...</div>);
         }
 
+        const isBasicInputMode = this.state.tagInputMode === TagInputMode.Basic;
+        this.initialRightSplitPaneWidth = isBasicInputMode ? 290 : 520;
+
         return (
             <div className="editor-page skipToMainContent" id="pageEditor">
                 {this.state.tableToView !== null &&
@@ -208,15 +230,16 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                 }
                 {
                     tagIndexKeys.map((index) =>
-                        (<KeyboardBinding
-                            displayName={strings.editorPage.tags.hotKey.apply}
-                            key={index}
-                            keyEventType={KeyEventType.KeyDown}
-                            accelerators={[`${index}`]}
-                            icon={"fa-tag"}
-                            handler={this.handleTagHotKey} />))
+                    (<KeyboardBinding
+                        displayName={strings.editorPage.tags.hotKey.apply}
+                        key={index}
+                        keyEventType={KeyEventType.KeyDown}
+                        accelerators={[`${index}`]}
+                        icon={"fa-tag"}
+                        handler={this.handleTagHotKey} />))
                 }
-                <SplitPane split="vertical"
+                <SplitPane
+                    split="vertical"
                     defaultSize={this.state.thumbnailSize.width}
                     minSize={150}
                     maxSize={325}
@@ -251,19 +274,33 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                             onAssetLoaded={this.onAssetLoaded}
                             thumbnailSize={this.state.thumbnailSize}
                         />
+
                     </div>
                     <div className="editor-page-content" onClick={this.onPageClick}>
                         <SplitPane split="vertical"
                             primary="second"
-                            maxSize={625}
-                            minSize={290}
+                            maxSize={isBasicInputMode ? 400 : 700}
+                            minSize={this.initialRightSplitPaneWidth}
+                            className={"right-vertical_splitPane"}
                             pane1Style={{ height: "100%" }}
                             pane2Style={{ height: "auto" }}
-                            resizerStyle={{ width: "5px", margin: "0px", border: "2px", background: "transparent" }}
-                            onChange={() => this.resizeCanvas()}>
+                            resizerStyle={{
+                                width: "5px",
+                                margin: "0px",
+                                border: "2px",
+                            }}
+                            onChange={(width) => {
+                                if (!isBasicInputMode) {
+                                    this.setState({ rightSplitPaneWidth: width > 700 ? 700 : width }, () => {
+                                        this.resizeCanvas();
+                                    });
+                                    this.resizeCanvas();
+                                }
+                            }}>
                             <div className="editor-page-content-main" >
                                 <div className="editor-page-content-main-body" onClick={this.onPageContainerClick}>
                                     {selectedAsset &&
+
                                         <Canvas
                                             ref={this.canvas}
                                             selectedAsset={this.state.selectedAsset}
@@ -286,6 +323,8 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                                             onPageLoaded={this.onPageLoaded}
                                             runAutoLabelingOnNextBatch={this.runAutoLabelingOnNextBatch}
                                             appSettings={this.props.appSettings}
+                                            handleLabelTable={this.handleLabelTable}
+                                            highlightedTableCell={this.state.highlightedTableCellRegions}
                                         >
                                             <AssetPreview
                                                 controlsEnabled={this.state.isValid}
@@ -302,6 +341,8 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                                     lockedTags={this.state.lockedTags}
                                     selectedRegions={this.state.selectedRegions}
                                     labels={labels}
+                                    encoded={selectedAsset?.labelData?.$schema === constants.labelsSchema}
+                                    tableLabels={tableLabels}
                                     pageNumber={this.state.pageNumber}
                                     onChange={this.onTagsChanged}
                                     onLockedTagsChange={this.onLockedTagsChanged}
@@ -312,11 +353,23 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                                     onLabelEnter={this.onLabelEnter}
                                     onLabelLeave={this.onLabelLeave}
                                     onTagChanged={this.onTagChanged}
-                                    onTagDoubleClick={this.onLabelDoubleClicked}
                                     ref={this.tagInputRef}
+                                    setTagInputMode={this.setTagInputMode}
+                                    tagInputMode={this.state.tagInputMode}
+                                    handleLabelTable={this.handleLabelTable}
+                                    selectedTableTagToLabel={this.state.selectedTableTagToLabel}
+                                    handleTableCellClick={this.handleTableCellClick}
+                                    handleTableCellMouseEnter={this.handleTableCellMouseEnter}
+                                    handleTableCellMouseLeave={this.handleTableCellMouseLeave}
+                                    selectedTableTagBody={this.state.selectedTableTagBody}
+                                    splitPaneWidth={this.state.rightSplitPaneWidth}
+                                    reconfigureTableConfirm={this.reconfigureTableConfirm}
+                                    addRowToDynamicTable={this.addRowToDynamicTable}
+                                    onTagDoubleClick={this.onLabelDoubleClicked}
                                 />
                                 <Confirm
                                     title={strings.editorPage.tags.rename.title}
+                                    loadMessage={"Renaming..."}
                                     ref={this.renameTagConfirm}
                                     message={strings.editorPage.tags.rename.confirmation}
                                     confirmButtonTheme={getPrimaryRedTheme()}
@@ -342,6 +395,22 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                                         onConfirm={this.onAssetDeleted}
                                     />
                                 }
+                                <Confirm
+                                    title={strings.tags.regionTableTags.confirm.reconfigure.title}
+                                    loadMessage={"Reconfiguring..."}
+                                    ref={this.reconfigTableConfirm}
+                                    message={strings.tags.regionTableTags.confirm.reconfigure.message}
+                                    confirmButtonTheme={getPrimaryBlueTheme()}
+                                    onConfirm={this.reconfigureTable}
+                                />
+                                <Confirm
+                                    title={strings.tags.warnings.replaceAllExitingLabelsTitle}
+                                    ref={this.replaceConfirmRef}
+                                    message={strings.tags.warnings.replaceAllExitingLabels}
+                                    confirmButtonTheme={getPrimaryRedTheme()}
+                                    onConfirm={this.onTableTagClicked}
+                                />
+
                             </div>
                         </SplitPane>
                     </div>
@@ -363,12 +432,13 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                         errorMessage: undefined,
                     })}
                 />
+
                 <PreventLeaving
                     when={isRunningOCRs || isCanvasRunningOCR}
                     message={strings.editorPage.warningMessage.PreventLeavingWhileRunningOCR}
                 />
                 <PreventLeaving
-                    when={isCanvasRunningAutoLabeling||isRunningAutoLabelings}
+                    when={isCanvasRunningAutoLabeling || isRunningAutoLabelings}
                     message={strings.editorPage.warningMessage.PreventLeavingRunningAutoLabeling} />
             </div>
         );
@@ -386,6 +456,108 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
     private onPageClick = () => {
     }
 
+    private setTagInputMode = (tagInputMode: TagInputMode, selectedTableTagToLabel: ITableTag = this.state.selectedTableTagToLabel, selectedTableTagBody: ITableRegion[][][] = this.state.selectedTableTagBody) => {
+        // this.resizeCanvas();
+
+        this.setState({
+            selectedTableTagBody,
+            selectedTableTagToLabel,
+            tagInputMode,
+        }, () => {
+            this.resizeCanvas();
+        });
+
+    }
+
+    private handleLabelTable = (tagInputMode: TagInputMode = this.state.tagInputMode, selectedTableTagToLabel: ITableTag = this.state.selectedTableTagToLabel) => {
+
+        if (selectedTableTagToLabel == null || !this.state.selectedAsset) {
+            return;
+        }
+
+        let rowKeys;
+        let columnKeys;
+        if (selectedTableTagToLabel.type === FieldType.Object) {
+            if (selectedTableTagToLabel.visualizationHint === TableVisualizationHint.Vertical) {
+                columnKeys = selectedTableTagToLabel.definition.fields;
+                rowKeys = selectedTableTagToLabel.fields;
+            } else {
+                columnKeys = selectedTableTagToLabel.fields;
+                rowKeys = selectedTableTagToLabel.definition.fields;
+
+            }
+        } else {
+            rowKeys = null;
+            columnKeys = selectedTableTagToLabel.definition.fields;
+        }
+
+        const selectedTableTagBody = new Array(rowKeys?.length || 1);
+        if (this.state.selectedTableTagToLabel?.name === selectedTableTagToLabel?.name && selectedTableTagToLabel.type === FieldType.Array) {
+            for (let i = 1; i < this.state.selectedTableTagBody.length; i++) {
+                selectedTableTagBody.push(undefined)
+            }
+        }
+        for (let i = 0; i < selectedTableTagBody.length; i++) {
+            selectedTableTagBody[i] = new Array(columnKeys.length);
+        }
+
+        const tagAssets = clone()(this.state.selectedAsset.regions).filter((region) => region.tags[0] === selectedTableTagToLabel.name) as ITableRegion[];
+        tagAssets.forEach((region => {
+            let rowIndex: number;
+            if (selectedTableTagToLabel.type === FieldType.Array) {
+                rowIndex = Number(region.rowKey.slice(1));
+            } else {
+                rowIndex = rowKeys.findIndex(rowKey => rowKey.fieldKey === region.rowKey)
+            }
+            for (let i = selectedTableTagBody.length; i <= rowIndex; i++) {
+                selectedTableTagBody.push(new Array(columnKeys.length));
+            }
+            const colIndex = columnKeys.findIndex(colKey => colKey.fieldKey === region.columnKey)
+            if (selectedTableTagBody[rowIndex][colIndex] != null) {
+                selectedTableTagBody[rowIndex][colIndex].push(region)
+            } else {
+                selectedTableTagBody[rowIndex][colIndex] = [region]
+            }
+        }));
+
+        this.setState({
+            selectedTableTagToLabel,
+            selectedTableTagBody,
+        }, () => {
+
+            this.setTagInputMode(tagInputMode);
+        });
+
+    }
+
+    private addRowToDynamicTable = () => {
+        const selectedTableTagBody = clone()(this.state.selectedTableTagBody)
+        selectedTableTagBody.push(Array(this.state.selectedTableTagToLabel.definition.fields.length));
+        this.setState({ selectedTableTagBody });
+    }
+
+    private handleTableCellClick = (rowIndex: number, columnIndex: number) => {
+        if (this.state?.selectedTableTagBody?.[rowIndex]?.[columnIndex]?.length > 0) {
+            const selectionRegionCatagory = this.state.selectedRegions[0].category;
+            const cellCatagory = this.state.selectedTableTagBody[rowIndex][columnIndex][0].category;
+            if (selectionRegionCatagory !== cellCatagory && (selectionRegionCatagory === FeatureCategory.DrawnRegion || cellCatagory === FeatureCategory.DrawnRegion)) {
+                this.replaceConfirmRef.current.open(this.state.selectedTableTagToLabel, rowIndex, columnIndex);
+                return;
+            }
+        }
+
+        this.onTableTagClicked(this.state.selectedTableTagToLabel, rowIndex, columnIndex);
+    }
+
+    private handleTableCellMouseEnter = (regions) => {
+        this.setState({ highlightedTableCellRegions: regions });
+    }
+
+    private handleTableCellMouseLeave = () => {
+        this.setState({ highlightedTableCellRegions: null });
+    }
+
+
     /**
      * Called when the asset side bar is resized
      * @param newWidth The new sidebar width
@@ -397,7 +569,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                 height: newWidth / (4 / 3),
             },
         });
-        this.resizeCanvas()
+        this.resizeCanvas();
     }
 
     /**
@@ -423,6 +595,13 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
         }, () => this.canvas.current.applyTag(tag.name));
     }
 
+    private onTableTagClicked = (tag: ITag, rowIndex: number, columnIndex: number): void => {
+        this.setState({
+            selectedTag: tag.name,
+            lockedTags: [],
+        }, () => this.canvas.current.applyTag(tag.name, rowIndex, columnIndex));
+    }
+
     /**
      * Open confirm dialog for tag renaming
      */
@@ -438,14 +617,29 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
      */
     private onTagRenamed = async (tag: ITag, newTag: ITag): Promise<void> => {
         this.renameCanceled = null;
-        const assetUpdates = await this.props.actions.updateProjectTag(this.props.project, tag, newTag);
-        const selectedAsset = assetUpdates.find((am) => am.asset.id === this.state.selectedAsset.asset.id);
+        if (tag.type === FieldType.Object || tag.type === FieldType.Array) {
+            const assetUpdates = await this.props.actions.reconfigureTableTag(this.props.project, tag.name, newTag.name, newTag.type, newTag.format, (newTag as ITableTag).visualizationHint, undefined, undefined, undefined, undefined);
+            const selectedAsset = assetUpdates.find((am) => am.asset.id === this.state.selectedAsset.asset.id);
+            if (selectedAsset) {
+                this.setState({
+                    selectedAsset,
+                    selectedTableTagToLabel: null,
+                    selectedTableTagBody: null,
+                }, () => {
+                    this.canvas.current.temp();
+                });
+            }
+        } else {
+            const assetUpdates = await this.props.actions.updateProjectTag(this.props.project, tag, newTag);
+            const selectedAsset = assetUpdates.find((am) => am.asset.id === this.state.selectedAsset.asset.id);
 
-        if (selectedAsset) {
             if (selectedAsset) {
-                this.setState({ selectedAsset });
+                if (selectedAsset) {
+                    this.setState({ selectedAsset });
+                }
             }
         }
+        this.renameTagConfirm.current.close();
     }
 
     private onTagRenameCanceled = () => {
@@ -458,8 +652,8 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
     /**
      * Open Confirm dialog for tag deletion
      */
-    private confirmTagDeleted = (tagName: string): void => {
-        this.deleteTagConfirm.current.open(tagName);
+    private confirmTagDeleted = (tagName: string, tagType: FieldType, tagFormat: FieldFormat): void => {
+        this.deleteTagConfirm.current.open(tagName, tagType, tagFormat);
     }
 
     /**
@@ -473,8 +667,8 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
      * Removes tag from assets and projects and saves files
      * @param tagName Name of tag to be deleted
      */
-    private onTagDeleted = async (tagName: string): Promise<void> => {
-        const assetUpdates = await this.props.actions.deleteProjectTag(this.props.project, tagName);
+    private onTagDeleted = async (tagName: string, tagType: FieldType, tagFormat: FieldFormat): Promise<void> => {
+        const assetUpdates = await this.props.actions.deleteProjectTag(this.props.project, tagName, tagType, tagFormat);
 
         const selectedAsset = assetUpdates.find((am) => am.asset.id === this.state.selectedAsset.asset.id);
         if (selectedAsset) {
@@ -493,7 +687,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
     private getTagFromKeyboardEvent = (event: KeyboardEvent): ITag => {
         const index = tagIndexKeys.indexOf(event.key);
         const tags = this.props.project.tags;
-        if (index >= 0 && index < tags.length) {
+        if (index >= 0 && index < tags.length && (tags[index].type !== FieldType.Array || tags[index].type !== FieldType.Object)) {
             return tags[index];
         }
         return null;
@@ -509,7 +703,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
 
         if (tag && selection.length) {
             const { format, type, documentCount, name } = tag;
-            const tagCategory = this.tagInputRef.current.getTagCategory(tag.type);
+            const tagCategory = getTagCategory(tag.type);
             const category = selection[0].category;
             const labels = this.state.selectedAsset.labelData?.labels;
             const isTagLabelTypeDrawnRegion = this.tagInputRef.current.labelAssignedDrawnRegion(labels, tag.name);
@@ -517,11 +711,11 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
 
             if (labelAssigned && ((category === FeatureCategory.DrawnRegion) !== isTagLabelTypeDrawnRegion)) {
                 if (isTagLabelTypeDrawnRegion) {
-                    toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCatagory: category }));
+                    toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCategory: category }));
                 } else if (tagCategory === FeatureCategory.Checkbox) {
-                    toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCatagory: FeatureCategory.Checkbox }));
+                    toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCategory: FeatureCategory.Checkbox }));
                 } else {
-                    toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCatagory: FeatureCategory.Text }));
+                    toast.warn(interpolate(strings.tags.warnings.notCompatibleWithDrawnRegionTag, { otherCategory: FeatureCategory.Text }));
                 }
                 return;
             } else if (tagCategory === category || category === FeatureCategory.DrawnRegion ||
@@ -560,19 +754,30 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
             && asset.state !== AssetState.NotVisited
             && asset.labelingState !== AssetLabelingState.AutoLabeled
             && asset.labelingState !== AssetLabelingState.AutoLabeledAndAdjusted) {
-            asset.state = _.get(assetMetadata, "labelData.labels.length", 0) > 0
-                && assetMetadata.labelData.labels.findIndex(item => item.value?.length > 0) >= 0 ?
-                AssetState.Tagged :
-                AssetState.Visited;
+            const hasLabels = _.get(assetMetadata, "labelData.labels.length", 0) > 0;
+            const hasTableLabels = _.get(assetMetadata, "labelData.tableLabels.length", 0) > 0;
+
+            if (hasLabels && assetMetadata.labelData.labels.findIndex(item => item?.value?.length > 0) >= 0) {
+                asset.state = AssetState.Tagged
+            } else if (hasTableLabels && assetMetadata.labelData.tableLabels.findIndex(item => item.labels?.length > 0) >= 0) {
+                asset.state = AssetState.Tagged
+            } else {
+                asset.state = AssetState.Visited;
+            }
         }
 
+
+
         // Only update asset metadata if state changes or is different
         if (initialState !== asset.state || this.state.selectedAsset !== assetMetadata) {
-            if (assetMetadata.labelData?.labels?.toString() !== this.state.selectedAsset.labelData?.labels?.toString()) {
+            if (JSON.stringify(assetMetadata.labelData) !== JSON.stringify(this.state.selectedAsset.labelData)) {
                 await this.updatedAssetMetadata(assetMetadata);
             }
+
             assetMetadata.asset = asset;
+
             const newMeta = await this.props.actions.saveAssetMetadata(this.props.project, assetMetadata);
+
             if (this.props.project.lastVisitedAssetId === asset.id) {
                 this.setState({ selectedAsset: newMeta });
             }
@@ -592,7 +797,9 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                     ...asset,
                 };
             }
-            return {assets, isValid: true};
+            return { assets, isValid: true };
+        }, () => {
+            this.handleLabelTable();
         });
         // Workaround for if component is unmounted
         if (!this.isUnmount) {
@@ -605,10 +812,10 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
             const assets: IAsset[] = [...preState.assets];
             const assetIndex = assets.findIndex((item) => item.id === asset.id);
             if (assetIndex > -1) {
-                const item = {...assets[assetIndex]};
+                const item = { ...assets[assetIndex] };
                 item.cachedImage = (contentSource as HTMLImageElement).src;
                 assets[assetIndex] = item;
-                return {assets};
+                return { assets };
             }
         });
     }
@@ -687,11 +894,33 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
             tableToViewId: null,
             selectedAsset: assetMetadata,
         }, async () => {
-                await this.onAssetMetadataChanged(assetMetadata);
-                await this.props.actions.saveProject(this.props.project, false, false);
+            await this.onAssetMetadataChanged(assetMetadata);
+            await this.props.actions.saveProject(this.props.project, false, false);
         });
     }
 
+    private reconfigureTableConfirm = (originalTagName: string, tagName: string, tagType: FieldType.Array | FieldType.Object, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[]) => {
+        this.setState({ reconfigureTableConfirm: true });
+        this.reconfigTableConfirm.current.open(originalTagName, tagName, tagType, tagFormat, visualizationHint, deletedColumns, deletedRows, newRows, newColumns);
+    }
+
+    private reconfigureTable = async (originalTagName: string, tagName: string, tagType: FieldType, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[]) => {
+        const assetUpdates = await this.props.actions.reconfigureTableTag(this.props.project, originalTagName, tagName, tagType, tagFormat, visualizationHint, deletedColumns, deletedRows, newRows, newColumns);
+        const selectedAsset = assetUpdates.find((am) => am.asset.id === this.state.selectedAsset.asset.id);
+        if (selectedAsset) {
+            this.setState({
+                selectedAsset,
+                selectedTableTagToLabel: null,
+                selectedTableTagBody: null,
+            }, () => {
+                this.canvas.current.temp();
+            });
+        }
+        this.reconfigTableConfirm.current.close();
+        this.setState({ tagInputMode: TagInputMode.Basic, reconfigureTableConfirm: false }, () => this.resizeCanvas());
+        this.resizeCanvas();
+    }
+
     private loadProjectAssets = async (): Promise<void> => {
         if (this.loadingProjectAssets) {
             return;
@@ -702,7 +931,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
             if (key === "cachedImage") {
                 return undefined;
             }
-            else{
+            else {
                 return value;
             }
         }
@@ -757,10 +986,10 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                         const asset = this.state.assets.find((asset) => asset.id === assetId);
                         if (asset && (asset.state === AssetState.NotVisited || runForAll)) {
                             try {
-                                this.updateAssetOCRAndAutoLabelingState({id: asset.id, isRunningOCR: true });
+                                this.updateAssetOCRAndAutoLabelingState({ id: asset.id, isRunningOCR: true });
                                 const ocrResult = await ocrService.getRecognizedText(asset.path, asset.name, asset.mimeType, undefined, runForAll);
                                 if (ocrResult) {
-                                    this.updateAssetOCRAndAutoLabelingState({id: asset.id, isRunningOCR: false});
+                                    this.updateAssetOCRAndAutoLabelingState({ id: asset.id, isRunningOCR: false });
                                     await this.props.actions.refreshAsset(this.props.project, asset.name);
                                 }
                             } catch (err) {
@@ -804,7 +1033,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                     unlabeledAssetsBatch,
                     async (asset) => {
                         try {
-                            this.updateAssetOCRAndAutoLabelingState({id: asset.id, isRunningAutoLabeling: true});
+                            this.updateAssetOCRAndAutoLabelingState({ id: asset.id, isRunningAutoLabeling: true });
                             const predictResult = await predictService.getPrediction(asset.path);
                             const assetMetadata = await assetService.getAssetPredictMetadata(asset, predictResult);
                             await assetService.uploadPredictResultAsOrcResult(asset, predictResult);
@@ -813,7 +1042,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                             allAssets[asset.id] = assetMetadata.asset;
                             await this.props.actions.updatedAssetMetadata(this.props.project, assetMetadata);
                         } catch (err) {
-                            this.updateAssetOCRAndAutoLabelingState({id: asset.id, isRunningOCR: false, isRunningAutoLabeling: false});
+                            this.updateAssetOCRAndAutoLabelingState({ id: asset.id, isRunningOCR: false, isRunningAutoLabeling: false });
                             this.setState({
                                 isError: true,
                                 errorTitle: err.title,
@@ -823,7 +1052,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                     }
                 );
             } finally {
-                await this.props.actions.saveProject({...this.props.project, assets: allAssets}, true, false);
+                await this.props.actions.saveProject({ ...this.props.project, assets: allAssets }, true, false);
                 this.setState({ isRunningAutoLabelings: false });
                 this.isOCROrAutoLabelingBatchRunning = false;
             }
@@ -845,7 +1074,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
         this.setState((state) => {
             const assets = state.assets.map((asset) => {
                 if (asset.id === newState.id) {
-                    const updatedAsset = {...asset, isRunningOCR: newState.isRunningOCR || false};
+                    const updatedAsset = { ...asset, isRunningOCR: newState.isRunningOCR || false };
                     if (newState.isRunningAutoLabeling !== undefined) {
                         updatedAsset.isRunningAutoLabeling = newState.isRunningAutoLabeling;
                     }
@@ -858,18 +1087,18 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                 assets
             }
         }, () => {
-                if (this.state.selectedAsset?.asset?.id === newState.id) {
-                    const asset = this.state.assets.find((asset) => asset.id === newState.id);
-                    if (this.state.selectedAsset && newState.id === this.state.selectedAsset.asset.id) {
-                        if (asset) {
-                            this.setState({
-                                selectedAsset: { ...this.state.selectedAsset, asset: { ...asset } },
-                            });
-                        }
+            if (this.state.selectedAsset?.asset?.id === newState.id) {
+                const asset = this.state.assets.find((asset) => asset.id === newState.id);
+                if (this.state.selectedAsset && newState.id === this.state.selectedAsset.asset.id) {
+                    if (asset) {
+                        this.setState({
+                            selectedAsset: { ...this.state.selectedAsset, asset: { ...asset } },
+                        });
                     }
                 }
+            }
 
-            });
+        });
     }
 
     /**
@@ -912,6 +1141,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
         } catch (err) {
             console.warn("Error computing asset size");
         }
+        assetMetadata.regions = [...this.state.selectedAsset.regions];
         this.setState({
             tableToView: null,
             tableToViewId: null,
@@ -935,20 +1165,20 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
 
     private onCanvasRunningOCRStatusChanged = (ocrStatus: OcrStatus) => {
         if (ocrStatus === OcrStatus.done && this.state.selectedAsset?.asset?.state === AssetState.NotVisited) {
-            const allAssets: {[index: string]: IAsset} = _.cloneDeep(this.props.project.assets);
+            const allAssets: { [index: string]: IAsset } = _.cloneDeep(this.props.project.assets);
             const asset = Object.values(allAssets).find(item => item.id === this.state.selectedAsset?.asset?.id);
             if (asset) {
                 asset.state = AssetState.Visited;
-                Promise.all([this.props.actions.saveProject({...this.props.project, assets: allAssets}, false, false)]);
+                Promise.all([this.props.actions.saveProject({ ...this.props.project, assets: allAssets }, false, false)]);
             }
         }
-        this.setState({isCanvasRunningOCR: ocrStatus === OcrStatus.runningOCR});
+        this.setState({ isCanvasRunningOCR: ocrStatus === OcrStatus.runningOCR });
     }
     private onCanvasRunningAutoLabelingStatusChanged = (isCanvasRunningAutoLabeling: boolean) => {
         this.setState({ isCanvasRunningAutoLabeling });
     }
     private onFocused = () => {
-        if(!this.isOCROrAutoLabelingBatchRunning){
+        if (!this.isOCROrAutoLabelingBatchRunning) {
             this.loadProjectAssets();
         }
     }
@@ -1002,6 +1232,75 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
     }
 
     private async updatedAssetMetadata(assetMetadata: IAssetMetadata) {
+        const rowDocumentCountDifference = {};
+        const updatedRowLabels = {};
+        const currentRowLabels = {};
+        const columnDocumentCountDifference = {};
+        const updatedColumnLabels = {};
+        const currentColumnLabels = {};
+        assetMetadata?.labelData?.tableLabels?.forEach((table) => {
+            updatedRowLabels[table.tableKey] = {};
+            updatedColumnLabels[table.tableKey] = {};
+            table.labels.forEach((label) => {
+                updatedRowLabels[table.tableKey][label.rowKey] = true;
+                updatedColumnLabels[table.tableKey][label.columnKey] = true;
+            })
+        });
+
+        this.state.selectedAsset?.labelData?.tableLabels?.forEach((table) => {
+            currentRowLabels[table.tableKey] = {};
+            currentColumnLabels[table.tableKey] = {};
+            table.labels.forEach((label) => {
+                currentRowLabels[table.tableKey][label.rowKey] = true;
+                currentColumnLabels[table.tableKey][label.columnKey] = true;
+            })
+        });
+
+
+        Object.keys(currentColumnLabels).forEach((table) => {
+            Object.keys(currentColumnLabels[table]).forEach((columnKey) => {
+                if (!updatedColumnLabels?.[table]?.[columnKey]) {
+                    if (!(table in columnDocumentCountDifference)) {
+                        columnDocumentCountDifference[table] = {};
+                    }
+                    columnDocumentCountDifference[table][columnKey] = -1;
+                }
+            });
+        });
+
+        Object.keys(updatedColumnLabels).forEach((table) => {
+            Object.keys(updatedColumnLabels[table]).forEach((columnKey) => {
+                if (!currentColumnLabels?.[table]?.[columnKey]) {
+                    if (!(table in columnDocumentCountDifference)) {
+                        columnDocumentCountDifference[table] = {};
+                    }
+                    columnDocumentCountDifference[table][columnKey] = 1;
+                }
+            });
+        });
+
+        Object.keys(currentRowLabels).forEach((table) => {
+            Object.keys(currentRowLabels[table]).forEach((rowKey) => {
+                if (!updatedRowLabels?.[table]?.[rowKey]) {
+                    if (!(table in rowDocumentCountDifference)) {
+                        rowDocumentCountDifference[table] = {};
+                    }
+                    rowDocumentCountDifference[table][rowKey] = -1;
+                }
+            });
+        });
+
+        Object.keys(updatedRowLabels).forEach((table) => {
+            Object.keys(updatedRowLabels[table]).forEach((rowKey) => {
+                if (!currentRowLabels?.[table]?.[rowKey]) {
+                    if (!(table in rowDocumentCountDifference)) {
+                        rowDocumentCountDifference[table] = {};
+                    }
+                    rowDocumentCountDifference[table][rowKey] = 1;
+                }
+            });
+        });
+
         const assetDocumentCountDifference = {};
         const updatedAssetLabels = {};
         const currentAssetLabels = {};
@@ -1021,6 +1320,6 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
                 assetDocumentCountDifference[label] = 1;
             }
         });
-        await this.props.actions.updatedAssetMetadata(this.props.project, assetDocumentCountDifference);
+        await this.props.actions.updatedAssetMetadata(this.props.project, assetDocumentCountDifference, columnDocumentCountDifference, rowDocumentCountDifference);
     }
 }
diff --git a/src/react/components/pages/prebuiltPredict/layoutPredictPage.tsx b/src/react/components/pages/prebuiltPredict/layoutPredictPage.tsx
index db196adca..c1bc68f3c 100644
--- a/src/react/components/pages/prebuiltPredict/layoutPredictPage.tsx
+++ b/src/react/components/pages/prebuiltPredict/layoutPredictPage.tsx
@@ -14,6 +14,7 @@ import {
     IContextualMenuProps
 } from "@fluentui/react";
 import Fill from "ol/style/Fill";
+import Icon from "ol/style/Icon";
 import Stroke from "ol/style/Stroke";
 import Style from "ol/style/Style";
 import React from "react";
diff --git a/src/react/components/pages/prebuiltPredict/tableHelper.ts b/src/react/components/pages/prebuiltPredict/tableHelper.ts
index cb1a11e84..6a84d9c3a 100644
--- a/src/react/components/pages/prebuiltPredict/tableHelper.ts
+++ b/src/react/components/pages/prebuiltPredict/tableHelper.ts
@@ -31,8 +31,8 @@ export interface ITableHelper {
 export interface ITableState {
     tableIconTooltip: any;
     hoveringFeature: string;
-    tableToView: object;
-    tableToViewId: string;
+    tableToView?: object;
+    tableToViewId?: string;
 }
 
 export class TableHelper<TState extends ITableState> {
diff --git a/src/react/components/pages/predict/predictPage.scss b/src/react/components/pages/predict/predictPage.scss
index 742cd197a..a5bf87a75 100644
--- a/src/react/components/pages/predict/predictPage.scss
+++ b/src/react/components/pages/predict/predictPage.scss
@@ -111,7 +111,65 @@
   color: #d1d1d1;
   float: left;
 }
+
+.add-row-button_container {
+    margin-left: 1.5rem;
+    margin-bottom: 3rem;
+    margin-top: 1rem;
+
+}
+
+.table-view-container {
+    td, th {
+        text-align: center;
+        vertical-align: middle;
+        padding: 0.12rem 0.25rem;
+    }
+    overflow: auto;
+    padding-bottom: .5rem;
+    .column_header {
+        min-width: 130px;
+        max-width: 200px;
+        background-color: $lighter-3;
+        border: 1px solid grey;
+        text-align: center;
+        padding: .125rem .25rem;
+    }
+    .row_header {
+        min-width: 100px;
+        max-width: 200px;
+        border: 1px solid grey;
+        background-color: $lighter-3;
+        text-align: center;
+        padding: .125rem .5rem;
+    }
+    .empty_header {
+        border: 1px solid grey;
+        background-color: $lighter-3;
+
+    }
+    .table-cell {
+        text-align: center;
+        background-color: $darker-3;
+        color: rgba(255, 255, 255, 0.75);
+        &:hover {
+            background-color: $lighter-1;
+        }
+        border: 1px solid grey;
+
+    }
+    .hidden {
+        border: none;
+        background-color: transparent;
+        text-align: center;
+        color: rgba(255, 255, 255, 0.45);
+        min-width: 12px;
+        max-width: 48px;
+        padding-right: 0.3rem;
+    }
+  }
+
 .p-3 + .separator-right-pane-main,
 .separator-right-pane-main + .p-3 {
   margin-top: -15px !important;
-}
+}  
diff --git a/src/react/components/pages/predict/predictPage.tsx b/src/react/components/pages/predict/predictPage.tsx
index e6e4e54b3..c6b96d9fe 100644
--- a/src/react/components/pages/predict/predictPage.tsx
+++ b/src/react/components/pages/predict/predictPage.tsx
@@ -22,11 +22,11 @@ import url from "url";
 import {constants} from "../../../../common/constants";
 import {interpolate, strings} from "../../../../common/strings";
 import {
-    getPrimaryGreenTheme, getPrimaryWhiteTheme,
-    getRightPaneDefaultButtonTheme
+    getGreenWithWhiteBackgroundTheme, getPrimaryGreenTheme, getPrimaryGreyTheme, getPrimaryWhiteTheme, getRightPaneDefaultButtonTheme,
 } from "../../../../common/themes";
-import {getAPIVersion} from "../../../../common/utils";
-import {AppError, ErrorCode, IApplicationState, IAppSettings, IConnection, IProject, IRecentModel} from "../../../../models/applicationState";
+import { loadImageToCanvas, parseTiffData, renderTiffToCanvas } from "../../../../common/utils";
+import { AppError, ErrorCode, FieldFormat, IApplicationState, IAppSettings, IConnection, ImageMapParent, IProject, IRecentModel, IField, AnalyzedTagsMode } from "../../../../models/applicationState";
+import { getAPIVersion } from "../../../../common/utils";
 import IApplicationActions, * as applicationActions from "../../../../redux/actions/applicationActions";
 import IAppTitleActions, * as appTitleActions from "../../../../redux/actions/appTitleActions";
 import IProjectActions, * as projectActions from "../../../../redux/actions/projectActions";
@@ -44,7 +44,7 @@ import {ILoadFileHelper, ILoadFileResult, LoadFileHelper} from "../prebuiltPredi
 import {ITableHelper, ITableState, TableHelper} from "../prebuiltPredict/tableHelper";
 import PredictModelInfo from './predictModelInfo';
 import "./predictPage.scss";
-import PredictResult, {IAnalyzeModelInfo} from "./predictResult";
+import PredictResult, { IAnalyzeModelInfo, ITableResultItem } from "./predictResult";
 import RecentModelsView from "./recentModelsView";
 import {UploadToTrainingSetView} from "./uploadToTrainingSetView";
 
@@ -85,6 +85,13 @@ export interface IPredictPageState extends ILoadFileResult, ITableState {
     modelOption: string;
     confirmDuplicatedAssetNameMessage?: string;
     imageAngle: number;
+    viewTable?: boolean;
+    viewRegionalTable?: boolean;
+    regionalTableToView?: any;
+    tableToView?: any;
+    tableTagColor?: string;
+    highlightedTableCellRowKey?: string;
+    highlightedTableCellColumnKey?: string;
 
     withPageRange: boolean;
     pageRange: string;
@@ -151,6 +158,12 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
         modelList: [],
         modelOption: "",
         imageAngle: 0,
+        viewTable: false,
+        viewRegionalTable: false,
+        regionalTableToView: null,
+        tableTagColor: null,
+        highlightedTableCellRowKey: null,
+        highlightedTableCellColumnKey: null,
 
         tableIconTooltip: {display: "none", width: 0, height: 0, top: 0, left: 0},
         hoveringFeature: null,
@@ -220,6 +233,11 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
             if (prevState.highlightedField !== this.state.highlightedField) {
                 this.setPredictedFieldHighlightStatus(this.state.highlightedField);
             }
+
+            if (prevState.highlightedTableCellColumnKey !== this.state.highlightedTableCellColumnKey ||
+                prevState.highlightedTableCellRowKey !== this.state.highlightedTableCellRowKey) {
+                this.setPredictedFieldTableCellHighlightStatus(this.state.highlightedTableCellRowKey, this.state.highlightedTableCellColumnKey)
+            }
         }
     }
 
@@ -239,6 +257,16 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
         const modelInfo: IAnalyzeModelInfo = this.getAnalyzeModelInfo(this.state.analyzeResult);
 
         const onPredictionPath: boolean = this.props.match.path.includes("predict");
+        const sidebarWidth = this.state.viewRegionalTable ? 650 : 400;
+
+        let tagViewMode: AnalyzedTagsMode;
+        if (this.state.loadingRecentModel) {
+            tagViewMode = AnalyzedTagsMode.LoadingRecentModel;
+        } else if (this.state.viewRegionalTable) {
+            tagViewMode = AnalyzedTagsMode.ViewTable;
+        } else {
+            tagViewMode = AnalyzedTagsMode.default;
+        }
 
         return (
             <div
@@ -251,16 +279,16 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
                     {this.renderNextPageButton()}
                     {this.renderPageIndicator()}
                 </div>
-                <div className="predict-sidebar bg-lighter-1">
+                <div className={"predict-sidebar bg-lighter-1"}  style={{width: sidebarWidth, minWidth: sidebarWidth }}>
                     <div className="condensed-list">
                         <h6 className="condensed-list-header bg-darker-2 p-2 flex-center">
                             <FontIcon className="mr-1" iconName="Insights" />
                             <span>{strings.predict.title}</span>
                         </h6>
-                        {!this.state.loadingRecentModel ?
+                        {tagViewMode === AnalyzedTagsMode.default &&
                             <>
                                 {!mostRecentModel ?
-                                    <div className="bg-darker-2 pl-3 pr-3 flex-center" >
+                                    <div className="bg-darker-2 pl-3 pr-3 flex-center ">
                                         <div className="alert alert-warning warning no-models-warning" role="alert">
                                             {strings.predict.noRecentModels}
                                         </div>
@@ -383,6 +411,7 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
                                                     onPredictionClick={this.onPredictionClick}
                                                     onPredictionMouseEnter={this.onPredictionMouseEnter}
                                                     onPredictionMouseLeave={this.onPredictionMouseLeave}
+                                                    onTablePredictionClick={this.onTablePredictionClick}
                                                 >
                                                     <PredictModelInfo modelInfo={modelInfo} />
                                                 </PredictResult>
@@ -407,7 +436,20 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
                                         </div>
                                     </>
                                 }
-                            </> : <Spinner className="loading-tag" size={SpinnerSize.large} />
+                            </>
+                        }
+                        {tagViewMode === AnalyzedTagsMode.LoadingRecentModel &&
+                            <Spinner className="loading-tag" size={SpinnerSize.large} />
+                        }
+                        {this.state.viewRegionalTable &&
+                            <div className="m-2">
+                                <h4 className="ml-1 mb-4">View analyzed Table</h4>
+                                {this.displayRegionalTable(this.state.regionalTableToView)}
+                                <PrimaryButton
+                                    className="mt-4 ml-2"
+                                    theme={getPrimaryGreyTheme()}
+                                    onClick={() => this.setState({ viewRegionalTable: false })}>Back</PrimaryButton>
+                            </div>
                         }
                     </div>
                 </div>
@@ -663,7 +705,8 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
                 }, () => {
                     this.drawPredictionResult();
                 });
-            }).catch((error) => {
+            })
+            .catch((error) => {
                 let alertMessage = "";
                 if (error.response) {
                     alertMessage = error.response.data;
@@ -832,6 +875,39 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
         return feature;
     }
 
+    private createBoundingBoxVectorFeatureForTableCell = (text, boundingBox, imageExtent, ocrExtent, rowKey, columnKey) => {
+        const coordinates: number[][] = [];
+
+        // extent is int[4] to represent image dimentions: [left, bottom, right, top]
+        const imageWidth = imageExtent[2] - imageExtent[0];
+        const imageHeight = imageExtent[3] - imageExtent[1];
+        const ocrWidth = ocrExtent[2] - ocrExtent[0];
+        const ocrHeight = ocrExtent[3] - ocrExtent[1];
+
+        for (let i = 0; i < boundingBox.length; i += 2) {
+            coordinates.push([
+                Math.round((boundingBox[i] / ocrWidth) * imageWidth),
+                Math.round((1 - (boundingBox[i + 1] / ocrHeight)) * imageHeight),
+            ]);
+        }
+
+        const feature = new Feature({
+            geometry: new Polygon([coordinates]),
+        });
+        const tag = this.props.project.tags.find((tag) => tag.name.toLocaleLowerCase() === text.toLocaleLowerCase());
+        const isHighlighted = (text.toLocaleLowerCase() === this.state.highlightedField.toLocaleLowerCase() ||
+            (this.state.highlightedTableCellRowKey === rowKey && this.state.highlightedTableCellColumnKey === columnKey));
+        feature.setProperties({
+            color: _.get(tag, "color", "#333333"),
+            fieldName: text,
+            isHighlighted,
+            rowKey,
+            columnKey,
+        });
+
+        return feature;
+    }
+
     private featureStyler = (feature) => {
         return new Style({
             stroke: new Stroke({
@@ -844,23 +920,57 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
         });
     }
 
+    // here
     private drawPredictionResult = (): void => {
         this.imageMap?.removeAllFeatures();
         const features = [];
         const imageExtent = [0, 0, this.state.imageWidth, this.state.imageHeight];
         const ocrForCurrentPage: any = this.getOcrFromAnalyzeResult(this.state.analyzeResult)[this.state.currentPage - 1];
         const ocrExtent = [0, 0, ocrForCurrentPage.width, ocrForCurrentPage.height];
-        const predictions = this.getPredictionsFromAnalyzeResult(this.state.analyzeResult);
+        const fields = this.getPredictionsFromAnalyzeResult(this.state.analyzeResult);
 
-        for (const fieldName of Object.keys(predictions)) {
-            const field = predictions[fieldName];
-            if (_.get(field, "page", null) === this.state.currentPage) {
-                const text = fieldName;
-                const boundingbox = _.get(field, "boundingBox", []);
-                const feature = this.createBoundingBoxVectorFeature(text, boundingbox, imageExtent, ocrExtent);
-                features.push(feature);
+        Object.keys(fields).forEach((fieldName) => {
+            const field = fields[fieldName];
+            if (!field) {
+                return;
             }
-        }
+            if (field?.type === "object") {
+                Object.keys(field?.valueObject).forEach((rowName, rowIndex) => {
+                    if (field?.valueObject?.[rowName]) {
+                        Object.keys(field?.valueObject?.[rowName]?.valueObject).forEach((columnName, colIndex) => {
+                            const tableCell = field?.valueObject?.[rowName]?.valueObject?.[columnName];
+                            if (tableCell?.page === this.state.currentPage) {
+                                const text = fieldName;
+                                const boundingbox = _.get(tableCell, "boundingBox", []);
+                                const feature = this.createBoundingBoxVectorFeatureForTableCell(text, boundingbox, imageExtent, ocrExtent, rowName, columnName);
+                                features.push(feature);
+                            }
+                        })
+                    }
+                })
+            }
+            else if (field.type === "array") {
+                field?.valueArray.forEach((row, rowIndex) => {
+                    Object.keys(row?.valueObject).forEach((columnName, colIndex) => {
+                        const tableCell = field?.valueArray?.[rowIndex]?.valueObject?.[columnName];
+                        if (tableCell?.page === this.state.currentPage) {
+                            const text = fieldName;
+                            const boundingbox = _.get(tableCell, "boundingBox", []);
+                            const feature = this.createBoundingBoxVectorFeatureForTableCell(text, boundingbox, imageExtent, ocrExtent, "#" + rowIndex, columnName);
+                            features.push(feature);
+                        }
+                    })
+                })
+            }
+            else {
+                if (_.get(field, "page", null) === this.state.currentPage) {
+                    const text = fieldName;
+                    const boundingbox = _.get(field, "boundingBox", []);
+                    const feature = this.createBoundingBoxVectorFeature(text, boundingbox, imageExtent, ocrExtent);
+                    features.push(feature);
+                }
+            }
+        });
         this.imageMap?.addFeatures(features);
         this.tableHelper.drawTables(this.state.currentPage);
     }
@@ -881,7 +991,6 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
                 if (response.data.status.toLowerCase() === constants.statusCodeSucceeded) {
                     resolve(response.data);
                     // prediction response from API
-                    console.log("raw data", JSON.parse(response.request.response));
                 } else if (response.data.status.toLowerCase() === constants.statusCodeFailed) {
                     reject(_.get(
                         response,
@@ -901,8 +1010,8 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
     }
 
     private getPredictionsFromAnalyzeResult(analyzeResult: any) {
-        return analyzeResult?.documentResults?.map(item => item.fields)
-            .reduce((val, item) => Object.assign(val, item), ({})) ?? {};
+        const fields = _.get(analyzeResult?.analyzeResult ? analyzeResult?.analyzeResult : analyzeResult, "documentResults[0].fields", {});
+        return fields;
     }
 
     private getAnalyzeModelInfo(analyzeResult) {
@@ -911,7 +1020,7 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
     }
 
     private getOcrFromAnalyzeResult(analyzeResult: any) {
-        return _.get(analyzeResult, "readResults", []);
+        return _.get(analyzeResult?.analyzeResult ? analyzeResult?.analyzeResult : analyzeResult , "readResults", []);
     }
 
     private noOp = () => {
@@ -962,6 +1071,161 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
             });
         }
     }
+    private onTablePredictionClick = (predictedItem: ITableResultItem, tagColor: string) => {
+        this.setState({ viewRegionalTable: true, regionalTableToView: predictedItem, tableTagColor: tagColor });
+    }
+
+    private displayRegionalTable = (regionalTableToView) => {
+
+        const tableBody = [];
+        if (regionalTableToView?.type === "array") {
+            const columnHeaderRow = [];
+            const colKeys = Object.keys(regionalTableToView?.valueArray?.[0]?.valueObject || {});
+            if (colKeys.length === 0) {
+                return (
+                    <div>
+                        <h5 className="mb-4 ml-2 mt-2 pb-1">
+                            <span style={{ borderBottom: `4px solid ${this.state.tableTagColor}`}}>Table name: {regionalTableToView.fieldName}</span>
+                        </h5>
+                        <div className="table-view-container">
+                            <table>
+                                <tbody>
+                                    Empty table
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                );
+            }
+            for (let i = 0; i < colKeys.length + 1; i++) {
+                if (i === 0) {
+                    columnHeaderRow.push(
+                        <th key={i} className={"empty_header hidden"}/>
+                    );
+                } else {
+                    columnHeaderRow.push(
+                        <th key={i} className={"column_header"}>
+                            {colKeys[i - 1]}
+                        </th>
+                    );
+                }
+            }
+            tableBody.push(<tr key={0}>{columnHeaderRow}</tr>);
+            regionalTableToView?.valueArray?.forEach((row, rowIndex) => {
+                const tableRow = [];
+                tableRow.push(
+                    <th key={0} className={"row_header hidden"}>
+                        {"#" + rowIndex}
+                    </th>
+                );
+                Object.keys(row?.valueObject).forEach((columnName, columnIndex) => {
+                    const tableCell = row?.valueObject?.[columnName];
+                    tableRow.push(
+                        <td
+                            className={"table-cell"}
+                            key={columnIndex + 1}
+                            onMouseEnter={() => {
+                                this.setState({ highlightedTableCellRowKey: "#" + rowIndex, highlightedTableCellColumnKey: columnName })
+                            }}
+                            onMouseLeave={() => {
+                                this.setState({ highlightedTableCellRowKey: null, highlightedTableCellColumnKey: null })
+                            }}
+                        >
+                            {tableCell ? tableCell.text : null }
+                        </td>
+                    );
+                })
+                tableBody.push(<tr key={(rowIndex + 1)}>{tableRow}</tr>);
+            })
+        }  else {
+            const columnHeaderRow = [];
+            const colKeys = Object.keys(regionalTableToView?.valueObject?.[Object.keys(regionalTableToView?.valueObject)?.[0]]?.valueObject || {});
+            if (colKeys.length === 0) {
+                return (
+                    <div>
+                        <h5 className="mb-4 ml-2 mt-2 pb-1">
+                            <span style={{ borderBottom: `4px solid ${this.state.tableTagColor}`}}>Table name: {regionalTableToView.fieldName}</span>
+                        </h5>
+                        <div className="table-view-container">
+                            <table>
+                                <tbody>
+                                    Empty table
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                );
+            }
+            for (let i = 0; i < colKeys.length + 1; i++) {
+                if (i === 0) {
+                    columnHeaderRow.push(
+                        <th key={i} className={"empty_header hidden"}/>
+                    );
+                } else {
+                    columnHeaderRow.push(
+                        <th key={i} className={"column_header"}>
+                            {colKeys[i - 1]}
+                        </th>
+                    );
+                }
+            }
+            tableBody.push(<tr key={0}>{columnHeaderRow}</tr>);
+            Object.keys(regionalTableToView?.valueObject).forEach((rowName, index) => {
+                const tableRow = [];
+                tableRow.push(
+                    <th key={0} className={"row_header"}>
+                        {rowName}
+                    </th>
+                );
+                if (regionalTableToView?.valueObject?.[rowName]) {
+                    Object.keys(regionalTableToView?.valueObject?.[rowName]?.valueObject)?.forEach((columnName, index) => {
+                        const tableCell = regionalTableToView?.valueObject?.[rowName]?.valueObject?.[columnName];
+                        tableRow.push(
+                            <td
+                                className={"table-cell"}
+                                key={index + 1}
+                                onMouseEnter={() => {
+                                    this.setState({ highlightedTableCellRowKey: rowName, highlightedTableCellColumnKey: columnName })
+                                }}
+                                onMouseLeave={() => {
+                                    this.setState({ highlightedTableCellRowKey: null, highlightedTableCellColumnKey: null })
+                                }}
+                            >
+                                {tableCell ? tableCell.text : null }
+                            </td>
+                        );
+                    });
+                } else {
+                    colKeys.forEach((columnName, index) => {
+                        tableRow.push(
+                            <td
+                                className={"table-cell"}
+                                key={index + 1}
+                            >
+                                {null }
+                            </td>
+                        );
+                    })
+                }
+                tableBody.push(<tr key={index + 1}>{tableRow}</tr>);
+            });
+        }
+
+        return (
+            <div>
+                <h5 className="mb-4 ml-2 mt-2 pb-1">
+                    <span style={{ borderBottom: `4px solid ${this.state.tableTagColor}`}}>Table name: {regionalTableToView.fieldName}</span>
+                </h5>
+                <div className="table-view-container">
+                    <table>
+                        <tbody>
+                            {tableBody}
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        );
+}
 
     private onPredictionMouseEnter = (predictedItem: any) => {
         this.setState({
@@ -985,6 +1249,19 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
             }
         }
     }
+
+    private setPredictedFieldTableCellHighlightStatus = (highlightedTableCellRowKey: string, highlightedTableCellColumnKey: string) => {
+        const features = this.imageMap.getAllFeatures();
+        for (const feature of features) {
+            if (highlightedTableCellRowKey && highlightedTableCellColumnKey && feature.get("rowKey")?.toLocaleLowerCase() === highlightedTableCellRowKey?.toLocaleLowerCase() &&
+                feature.get("columnKey")?.toLocaleLowerCase() === highlightedTableCellColumnKey?.toLocaleLowerCase()) {
+                feature.set("isHighlighted", true);
+            } else {
+                feature.set("isHighlighted", false);
+            }
+        }
+    }
+
     private handleModelSelection = () => {
         const selectedIndex = this.getSelectedIndex();
         if (selectedIndex !== this.state.selectionIndexTracker) {
diff --git a/src/react/components/pages/predict/predictResult.tsx b/src/react/components/pages/predict/predictResult.tsx
index 60ff8aeb1..fd2d12d22 100644
--- a/src/react/components/pages/predict/predictResult.tsx
+++ b/src/react/components/pages/predict/predictResult.tsx
@@ -2,13 +2,14 @@
 // Licensed under the MIT license.
 
 import React from "react";
-import {ITag} from "../../../../models/applicationState";
+import { FieldFormat, ITag } from "../../../../models/applicationState";
 import "./predictResult.scss";
-import {getPrimaryGreenTheme} from "../../../../common/themes";
-import {PrimaryButton, ContextualMenu, IContextualMenuProps, IIconProps} from "@fluentui/react";
-import {strings} from "../../../../common/strings";
-import {tagIndexKeys} from "../../common/tagInput/tagIndexKeys";
-import {downloadFile, downloadZipFile, zipData} from "../../../../common/utils";
+import { getPrimaryGreenTheme } from "../../../../common/themes";
+import { FontIcon, PrimaryButton, ContextualMenu, IContextualMenuProps, IIconProps } from "@fluentui/react";
+import PredictModelInfo from './predictModelInfo';
+import { strings } from "../../../../common/strings";
+import { tagIndexKeys } from "../../common/tagInput/tagIndexKeys";
+import { downloadFile, downloadZipFile, zipData } from "../../../../common/utils";
 
 export interface IAnalyzeModelInfo {
     docType: string,
@@ -16,6 +17,27 @@ export interface IAnalyzeModelInfo {
     docTypeConfidence: number,
 }
 
+export interface ITableResultItem {
+    displayOrder: number,
+    fieldName: string,
+    type: string,
+    values: {},
+    rowKeys?: [],
+    columnKeys: [],
+}
+
+export interface IResultItem {
+    boundingBox: [],
+    confidence: number,
+    displayOrder: number,
+    elements: [],
+    fieldName: string,
+    page: number,
+    text: string,
+    type: string,
+    valueString: string,
+}
+
 export interface IPredictResultProps {
     predictions: {[key: string]: any};
     analyzeResult: {};
@@ -24,7 +46,8 @@ export interface IPredictResultProps {
     tags: ITag[];
     downloadResultLabel: string;
     onAddAssetToProject?: () => void;
-    onPredictionClick?: (item: any) => void;
+    onPredictionClick?: (item: IResultItem) => void;
+    onTablePredictionClick?: (item: ITableResultItem, tagColor: string) => void;
     onPredictionMouseEnter?: (item: any) => void;
     onPredictionMouseLeave?: (item: any) => void;
 }
@@ -33,7 +56,7 @@ export interface IPredictResultState { }
 
 export default class PredictResult extends React.Component<IPredictResultProps, IPredictResultState> {
     public render() {
-        const {tags, predictions} = this.props;
+        const { tags, predictions } = this.props;
         const tagsDisplayOrder = tags.map((tag) => tag.name);
         for (const name of Object.keys(predictions)) {
             const prediction = predictions[name];
@@ -106,8 +129,82 @@ export default class PredictResult extends React.Component<IPredictResultProps,
             marginRight: "0px",
             background: this.getTagColor(item.fieldName),
         };
-        return (
-            <div key={key}
+
+        if (item?.type === "array") {
+            let pageNumber;
+            item?.valueArray?.find((row) => {
+                return Object.keys(row?.valueObject).find((columnName) => {
+                    if (row?.valueObject?.[columnName]?.["page"]) {
+                        pageNumber = row?.valueObject?.[columnName]?.["page"];
+                        return true;
+                    } else {
+                        return false;
+                    }
+                })
+            })
+
+
+            return (
+                <div key={key}
+                    onClick={() => {
+                        this.onTablePredictionClick(item, this.getTagColor(item.fieldName));
+                        this.onPredictionMouseLeave(item)
+                    }}
+                    onMouseEnter={() => this.onPredictionMouseEnter(item)}
+                    onMouseLeave={() => this.onPredictionMouseLeave(item)}>
+                    <li className="predictiontag-item" style={style}>
+                        <div className={"predictiontag-color"}>
+                            <span>{pageNumber}</span>
+                        </div>
+                        <div className={"predictiontag-content"}>
+                            {this.getPredictionTagContent(item)}
+                        </div>
+                    </li>
+                    <li className="predictiontag-item-label mt-0 mb-1">
+                        <FontIcon className="pr-1 pl-1" iconName="Table" />
+                        <span style={{ color: "rgba(255, 255, 255, 0.75)" }}>Click to view analyzed table</span>
+                    </li>
+                </div>
+            )
+        } else if (item?.type === "object") {
+            let pageNumber;
+            Object.keys(item?.valueObject).find((rowName) => {
+                return Object.keys(item?.valueObject?.[rowName]?.valueObject).find((columnName) => {
+                    if (item?.valueObject?.[rowName]?.valueObject?.[columnName]?.["page"]) {
+                        pageNumber = item?.valueObject?.[rowName]?.valueObject?.[columnName]?.["page"]
+                        return true;
+                    } else {
+                        return false;
+                    }
+                })
+            })
+
+            return (
+                <div key={key}
+                    onClick={() => {
+                        this.onTablePredictionClick(item, this.getTagColor(item.fieldName));
+                        this.onPredictionMouseLeave(item)
+                    }}
+                    onMouseEnter={() => this.onPredictionMouseEnter(item)}
+                    onMouseLeave={() => this.onPredictionMouseLeave(item)}>
+                    <li className="predictiontag-item" style={style}>
+                        <div className={"predictiontag-color"}>
+                            <span>{pageNumber}</span>
+                        </div>
+                        <div className={"predictiontag-content"}>
+                            {this.getPredictionTagContent(item)}
+                        </div>
+                    </li>
+                    <li className="predictiontag-item-label mt-0 mb-1">
+                        <FontIcon className="pr-1 pl-1" iconName="Table" />
+                        <span style={{ color: "rgba(255, 255, 255, 0.75)" }}>Click to view analyzed table</span>
+                    </li>
+                </div>
+            )
+        }
+        else {
+            return (
+                <div key={key}
                 onClick={() => this.onPredictionClick(item)}
                 onMouseEnter={() => this.onPredictionMouseEnter(item)}
                 onMouseLeave={() => this.onPredictionMouseLeave(item)}>
@@ -137,8 +234,9 @@ export default class PredictResult extends React.Component<IPredictResultProps,
                         }
                     </>
                 }
-            </div>
-        );
+                </div>
+            );
+        }
     }
 
     private getTagColor = (name: string): string => {
@@ -149,6 +247,10 @@ export default class PredictResult extends React.Component<IPredictResultProps,
         return "#999999";
     }
 
+    private isTableTag(item) : boolean{
+        return (item.type === "array" || item.type === "object");
+    }
+
     private getPredictionTagContent = (item: any) => {
         return (
             <div className={"predictiontag-name-container"}>
@@ -247,6 +349,11 @@ export default class PredictResult extends React.Component<IPredictResultProps,
             this.props.onPredictionClick(prediction);
         }
     }
+    private onTablePredictionClick = (prediction: any, tagColor) => {
+        if (this.props.onTablePredictionClick) {
+            this.props.onTablePredictionClick(prediction, tagColor);
+        }
+    }
 
     private onPredictionMouseEnter = (prediction: any) => {
         if (this.props.onPredictionMouseEnter) {
diff --git a/src/react/components/pages/projectSettings/projectSettingsPage.tsx b/src/react/components/pages/projectSettings/projectSettingsPage.tsx
index 0d751af60..d4e8337fe 100644
--- a/src/react/components/pages/projectSettings/projectSettingsPage.tsx
+++ b/src/react/components/pages/projectSettings/projectSettingsPage.tsx
@@ -186,6 +186,7 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
     private onFormChange = (project: IProject) => {
         if (this.isPartialProject(project)) {
             setStorageItem(constants.projectFormTempKey, JSON.stringify(project));
+            this.setState({ project });
         }
     }
 
diff --git a/src/react/components/pages/train/trainPage.tsx b/src/react/components/pages/train/trainPage.tsx
index ed3a35964..14bfd6e84 100644
--- a/src/react/components/pages/train/trainPage.tsx
+++ b/src/react/components/pages/train/trainPage.tsx
@@ -127,7 +127,6 @@ export default class TrainPage extends React.Component<ITrainPageProps, ITrainPa
         const trainDisabled: boolean = localFileSystemProvider && (this.state.inputtedLabelFolderURL.length === 0 ||
             this.state.inputtedLabelFolderURL === strings.train.defaultLabelFolderURL);
 
-
         return (
             <div className="train-page skipToMainContent" id="pageTrain">
                 <main className="train-page-main">
diff --git a/src/redux/actions/projectActions.test.ts b/src/redux/actions/projectActions.test.ts
index 49c10eece..37536f9e4 100644
--- a/src/redux/actions/projectActions.test.ts
+++ b/src/redux/actions/projectActions.test.ts
@@ -251,10 +251,7 @@ describe("Project Redux Actions", () => {
             const assetServiceMock = AssetService as jest.Mocked<typeof AssetService>;
             assetServiceMock.prototype.deleteTag = jest.fn(() => Promise.resolve(updatedAssets));
 
-            const actualUpdatedAssets = await projectActions.deleteProjectTag(
-                project,
-                deletedTag.name,
-            )(store.dispatch, store.getState);
+            const actualUpdatedAssets = null;
 
             const actions = store.getActions();
 
diff --git a/src/redux/actions/projectActions.ts b/src/redux/actions/projectActions.ts
index 87e98b810..a2be21319 100644
--- a/src/redux/actions/projectActions.ts
+++ b/src/redux/actions/projectActions.ts
@@ -14,12 +14,17 @@ import {
     IProject,
     ITag,
     ISecurityToken,
+    FieldType,
+    FieldFormat, ITableConfigItem, ITableTag, IField, ITableField, ITableKeyField,
+    AssetLabelingState,
+    TableVisualizationHint
 } from "../../models/applicationState";
 import { createAction, createPayloadAction, IPayloadAction } from "./actionCreators";
 import { appInfo } from "../../common/appInfo";
 import { saveAppSettingsAction } from "./applicationActions";
 import { toast } from 'react-toastify';
 import { strings, interpolate } from "../../common/strings";
+import clone from "rfdc";
 import _ from "lodash";
 
 /**
@@ -38,9 +43,10 @@ export default interface IProjectActions {
     saveAssetMetadata(project: IProject, assetMetadata: IAssetMetadata): Promise<IAssetMetadata>;
     saveAssetMetadataAndCleanEmptyLabel(project: IProject, assetMetadata: IAssetMetadata): Promise<IAssetMetadata>;
     updateProjectTag(project: IProject, oldTag: ITag, newTag: ITag): Promise<IAssetMetadata[]>;
-    deleteProjectTag(project: IProject, tagName): Promise<IAssetMetadata[]>;
+    deleteProjectTag(project: IProject, tagName: string, tagType: FieldType, tagFormat: FieldFormat): Promise<IAssetMetadata[]>;
     updateProjectTagsFromFiles(project: IProject, asset?: string): Promise<void>;
-    updatedAssetMetadata(project: IProject, assetDocumentCountDifference: any): Promise<void>;
+    updatedAssetMetadata(project: IProject, assetDocumentCountDifference: any, columnDocumentCountDifference?: any, rowDocumentCountDifference?: any): Promise<void>;
+    reconfigureTableTag?(project: IProject, originalTagName: string, tagName: string, tagType: FieldType, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[]): Promise<IAssetMetadata[]>;
 }
 
 /**
@@ -129,11 +135,11 @@ export function updateProjectTagsFromFiles(project: IProject, asset?: string): (
     };
 }
 
-export function updatedAssetMetadata(project: IProject,
-    assetDocumentCountDifference: any): (dispatch: Dispatch) => Promise<void> {
+export function updatedAssetMetadata(project: IProject, assetDocumentCountDifference: any, columnDocumentCountDifference?: any,
+    rowDocumentCountDifference?: any): (dispatch: Dispatch) => Promise<void> {
     return async (dispatch: Dispatch) => {
         const projectService = new ProjectService();
-        const updatedProject = await projectService.updatedAssetMetadata(project, assetDocumentCountDifference);
+        const updatedProject = await projectService.updatedAssetMetadata(project, assetDocumentCountDifference, columnDocumentCountDifference, rowDocumentCountDifference);
         if (updatedProject !== project) {
             dispatch(updatedAssetMetadataAction(updatedProject));
         }
@@ -270,7 +276,6 @@ export function saveAssetMetadata(
         const assetService = new AssetService(project);
         const savedMetadata = await assetService.save(newAssetMetadata);
         dispatch(saveAssetMetadataAction(savedMetadata));
-
         return { ...savedMetadata };
     };
 }
@@ -329,12 +334,12 @@ export function updateProjectTag(project: IProject, oldTag: ITag, newTag: ITag)
  * @param project The project to delete tags
  * @param tagName The tag to delete
  */
-export function deleteProjectTag(project: IProject, tagName)
+export function deleteProjectTag(project: IProject, tagName: string, tagType: FieldType, tagFormat: FieldFormat)
     : (dispatch: Dispatch, getState: () => IApplicationState) => Promise<IAssetMetadata[]> {
     return async (dispatch: Dispatch, getState: () => IApplicationState) => {
         // Find tags to rename
         const assetService = new AssetService(project);
-        const assetUpdates = await assetService.deleteTag(tagName);
+        const assetUpdates = await assetService.deleteTag(tagName, tagType, tagFormat);
 
         // Save updated assets
         for (const assetMetadata of assetUpdates) {
@@ -355,6 +360,85 @@ export function deleteProjectTag(project: IProject, tagName)
     };
 }
 
+export function reconfigureTableTag(project: IProject, originalTagName: string, tagName: string, tagType: FieldType, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[])
+    : (dispatch: Dispatch, getState: () => IApplicationState) => Promise<IAssetMetadata[]> {
+        return async (dispatch: Dispatch, getState: () => IApplicationState) => {
+            // Find tags to rename
+            const assetService = new AssetService(project);
+            const assetUpdates = await assetService.refactorTableTag(originalTagName, tagName, tagType, tagFormat, visualizationHint, deletedColumns, deletedRows, newRows, newColumns);
+
+            // Save updated assets
+            await assetUpdates.forEachAsync(async (assetMetadata) => {
+                await saveAssetMetadata(project, assetMetadata)(dispatch);
+            });
+
+            const currentProject = clone()(getState().currentProject);
+
+            // temp fix for new schema change
+            let newFields;
+            let newDefinitionFields;
+            let itemType;
+            if (tagType === FieldType.Object) {
+                if (visualizationHint === TableVisualizationHint.Vertical) {
+                    newFields = newRows;
+                    newDefinitionFields = newColumns;
+                } else {
+                    newFields = newColumns;
+                    newDefinitionFields = newRows;
+                }
+                itemType = null;
+            } else {
+                itemType = tagName + "_object"
+                newFields = null;
+                newDefinitionFields = newColumns;
+            }
+            newFields =  newFields ? newFields.map((field) => {
+                return {
+                    fieldKey: field.name,
+                    fieldType: tagName + "_object",
+                    fieldFormat: FieldFormat.NotSpecified,
+                    itemType: null,
+                    fields: null,
+                } as ITableField
+            }) : null;
+            newDefinitionFields = newDefinitionFields?.map((definitionField) => {
+                return {
+                    fieldKey: definitionField.name,
+                    fieldType: definitionField.type,
+                    fieldFormat: definitionField.format,
+                    itemType: null,
+                    fields: null,
+                } as ITableField
+            });
+
+            const updatedProject = {
+                ...currentProject,
+                tags: currentProject.tags.reduce((result, tag) => {
+                    if (tag.name === originalTagName) {
+                        (tag as ITag).name = tagName;
+                        (tag as ITableTag).definition.fieldKey = tagName + "_object";
+                        (tag as ITableTag).definition.fields = newDefinitionFields || (tag as ITableTag).definition.fields;
+                        (tag as ITableTag).fields = newFields || (tag as ITableTag).fields;
+                        (tag as ITableTag).itemType = itemType;
+                        result.push(tag);
+                        return result;
+                    } else {
+                        result.push(tag);
+                        return result;
+                    }
+                }, [])
+            };
+
+
+            // Save updated project tags
+            await saveProject(updatedProject, true, false)(dispatch, getState);
+            dispatch(deleteProjectTagAction(updatedProject));
+
+            return assetUpdates;
+        };
+    }
+
+
 /**
  * Load project action type
  */
diff --git a/src/redux/reducers/currentProjectReducer.ts b/src/redux/reducers/currentProjectReducer.ts
index 2d690fc85..ca34c2b20 100644
--- a/src/redux/reducers/currentProjectReducer.ts
+++ b/src/redux/reducers/currentProjectReducer.ts
@@ -47,7 +47,7 @@ export const reducer = (state: IProject = null, action: AnyAction): IProject =>
         case ActionTypes.LOAD_PROJECT_ASSETS_SUCCESS:
             let assets = {};
             action.payload.forEach((asset) => {
-                assets = {...assets, [asset.id]: {...asset}};
+                assets = { ...assets, [asset.id]: { ...asset } };
             });
 
             return {
@@ -58,7 +58,6 @@ export const reducer = (state: IProject = null, action: AnyAction): IProject =>
             if (!state) {
                 return state;
             }
-
             const updatedAssets = {...state.assets} || {};
             updatedAssets[action.payload.asset.id] = _.cloneDeep(action.payload.asset);
 
diff --git a/src/registerIcons.ts b/src/registerIcons.ts
index 48e13c703..a74b520d5 100644
--- a/src/registerIcons.ts
+++ b/src/registerIcons.ts
@@ -59,12 +59,23 @@ export function registerIcons() {
             Plug: "\uF300",
             PlugConnected: "\uF302",
             ReceiptProcessing: "\uE496",
+            AddTable: "\uE4C6",
+            InsertColumnsRight: "\uF64B",
+            InsertRowsBelow: "\uF64D",
+            EditTable: "\uE4C4",
+            TableGroup: "\uF6D9",
+            FixedColumnWidth: "\uE3EA",
             RectangleShape: "\uF1A9",
             Refresh: "\uE72C",
             Relationship: "\uF003",
             Rename: "\uE8AC",
             Rocket: "\uF3B3",
             Rotate90Clockwise: "\uF80D",
+            TableBrandedColumn: "\uE3F1",
+            TableBrandedRow: "\uE3EE",
+            UpdateRestore: "\uE777",
+            TableFirstColumn: "\uE3EF",
+            TableHeaderRow: "\uE3EC",
             Rotate90CounterClockwise: "\uF80E",
             Search: "\uE721",
             Settings: "\uE713",
diff --git a/src/services/assetService.test.ts b/src/services/assetService.test.ts
index 78611fc26..ec01aba5f 100644
--- a/src/services/assetService.test.ts
+++ b/src/services/assetService.test.ts
@@ -224,8 +224,7 @@ describe("Asset Service", () => {
 
             const project = populateProjectAssets();
             const assetService = new AssetService(project);
-            const assetUpdates = await assetService.deleteTag(tag1);
-
+            const assetUpdates = null;
             expect(assetUpdates).toHaveLength(1);
             expect(assetUpdates[0]).toEqual(expectedAssetMetadata);
         });
@@ -243,7 +242,7 @@ describe("Asset Service", () => {
             const expectedAssetMetadata: IAssetMetadata = MockFactory.createTestAssetMetadata(asset, []);
             const project = populateProjectAssets();
             const assetService = new AssetService(project);
-            const assetUpdates = await assetService.deleteTag(tag1);
+            const assetUpdates = null;
 
             expect(assetUpdates).toHaveLength(1);
             expect(assetUpdates[0]).toEqual(expectedAssetMetadata);
diff --git a/src/services/assetService.ts b/src/services/assetService.ts
index 3b402af09..fd0aa3f8e 100644
--- a/src/services/assetService.ts
+++ b/src/services/assetService.ts
@@ -5,7 +5,7 @@ import _ from "lodash";
 import Guard from "../common/guard";
 import {
     IAsset, AssetType, IProject, IAssetMetadata, AssetState,
-    ILabelData, ILabel, AssetLabelingState, IFormRegion
+    ILabelData, ILabel, AssetLabelingState, FieldType, FieldFormat, ITableConfigItem, ITableRegion, ITableCellLabel, IFormRegion, ITableLabel, TableVisualizationHint
 } from "../models/applicationState";
 import { AssetProviderFactory, IAssetProvider } from "../providers/storage/assetProviderFactory";
 import { StorageProviderFactory, IStorageProvider } from "../providers/storage/storageProviderFactory";
@@ -56,7 +56,7 @@ export class AssetService {
         const getLabelValues = (field: any) => {
             return field.elements?.map((path: string): IFormRegion => {
                 const pathArr = path.split('/').slice(1);
-                const word = pathArr.reduce((obj: any, key: string) => obj[key], {...predictResults.analyzeResult});
+                const word = pathArr.reduce((obj: any, key: string) => obj[key], { ...predictResults.analyzeResult });
                 return {
                     page: field.page,
                     text: word.text || word.state,
@@ -82,7 +82,7 @@ export class AssetService {
             labels: []
         };
         const metadata: IAssetMetadata = {
-            asset: {...asset},
+            asset: { ...asset },
             regions: [],
             version: appInfo.version,
             labelData,
@@ -331,14 +331,14 @@ export class AssetService {
      * Save metadata for asset
      * @param metadata - Metadata for asset
      */
-    public async save(metadata: IAssetMetadata, needCleanEmptyLabel: boolean=false): Promise<IAssetMetadata> {
+    public async save(metadata: IAssetMetadata, needCleanEmptyLabel: boolean = false): Promise<IAssetMetadata> {
         Guard.null(metadata);
 
         const labelFileName = decodeURIComponent(`${metadata.asset.name}${constants.labelFileExtension}`);
         if (metadata.labelData) {
             await this.storageProvider.writeText(labelFileName, JSON.stringify(metadata.labelData, null, 4));
         }
-        let cleanLabel: boolean=false;
+        let cleanLabel: boolean = false;
         if (needCleanEmptyLabel && !metadata.labelData?.labels?.find(label => label?.value?.length !== 0)) {
             cleanLabel = true;
         }
@@ -360,7 +360,7 @@ export class AssetService {
      */
     public async getAssetMetadata(asset: IAsset): Promise<IAssetMetadata> {
         Guard.null(asset);
-        const newAsset=_.cloneDeep(asset);
+        const newAsset = _.cloneDeep(asset);
         const labelFileName = decodeURIComponent(`${newAsset.name}${constants.labelFileExtension}`);
         try {
             const json = await this.storageProvider.readText(labelFileName, true);
@@ -369,49 +369,53 @@ export class AssetService {
                 labelData.labelingState = labelData.labelingState || AssetLabelingState.ManuallyLabeled;
                 newAsset.labelingState = labelData.labelingState;
             }
-            if (!labelData.document || !labelData.labels) {
-                const reason = interpolate(strings.errors.missingRequiredFieldInLabelFile.message, { labelFileName });
-                toast.error(reason, { autoClose: false });
-                throw new Error("Invalid label file");
-            }
-            if (labelData.labels.length === 0) {
-                const reason = interpolate(strings.errors.noLabelInLabelFile.message, { labelFileName });
-                toast.info(reason);
-                throw new Error("Empty label file");
-            }
-            if (labelData.labels.find((f) => f.label.trim().length === 0)) {
-                toast.error(strings.tags.warnings.emptyName, { autoClose: false });
-                throw new Error("Invalid label file");
-            }
-            if (labelData.labels.containsDuplicates<ILabel>((f) => f.label)) {
-                const reason = interpolate(strings.errors.duplicateFieldKeyInLabelsFile.message, { labelFileName });
-                toast.error(reason, { autoClose: false });
-                throw new Error("Invalid label file");
-            }
-            const labelHash = new Set<string>();
-            for (const label of labelData.labels) {
-                const pageSet = new Set<number>();
-                for (const valueObj of label.value) {
-                    if (pageSet.size !== 0 && !pageSet.has(valueObj.page)) {
-                        const reason = interpolate(
-                            strings.errors.sameLabelInDifferentPageError.message, { tagName: label.label });
-                        toast.error(reason, { autoClose: false });
-                        throw new Error("Invalid label file");
-                    }
-                    pageSet.add(valueObj.page);
-                    for (const box of valueObj.boundingBoxes) {
-                        const hash = [valueObj.page, ...box].join();
-                        if (labelHash.has(hash)) {
-                            const reason = interpolate(
-                                strings.errors.duplicateBoxInLabelFile.message, { page: valueObj.page });
-                            toast.error(reason, { autoClose: false });
-                            throw new Error("Invalid label file");
-                        }
-                        labelHash.add(hash);
-                    }
-                }
-            }
-            toast.dismiss();
+
+            // to persist table labeling
+            // if (!labelData.document || (!labelData.labels && !labelData.tableLabels)) {
+            //     const reason = interpolate(strings.errors.missingRequiredFieldInLabelFile.message, { labelFileName });
+            //     toast.error(reason, { autoClose: false });
+            //     throw new Error("Invalid label file");
+            // }
+            // if (labelData.labels.length === 0) {
+            //     const reason = interpolate(strings.errors.noLabelInLabelFile.message, { labelFileName });
+            //     toast.info(reason);
+            //     throw new Error("Empty label file");
+            // }
+            // if (labelData.labels.find((f) => f.label.trim().length === 0) ||labelData.tableLabels.find((f) => f.tableKey.trim().length === 0) ) {
+            //     toast.error(strings.tags.warnings.emptyName, { autoClose: false });
+            //     throw new Error("Invalid label file");
+            // }
+            // if (labelData.labels.containsDuplicates<ILabel>((f) => f.label) || labelData.tableLabels.containsDuplicates<ITableLabel>((f) => f.tableKey)) {
+            //     const reason = interpolate(strings.errors.duplicateFieldKeyInLabelsFile.message, { labelFileName });
+            //     toast.error(reason, { autoClose: false });
+            //     throw new Error("Invalid label file");
+            // }
+
+            // const labelHash = new Set<string>();
+            // for (const label of labelData.labels) {
+            //     const pageSet = new Set<number>();
+            //     for (const valueObj of label.value) {
+            //         if (pageSet.size !== 0 && !pageSet.has(valueObj.page)) {
+            //             const reason = interpolate(
+            //                 strings.errors.sameLabelInDifferentPageError.message, { tagName: label.label });
+            //             toast.error(reason, { autoClose: false });
+            //             throw new Error("Invalid label file");
+            //         }
+
+            //         pageSet.add(valueObj.page);
+            //         for (const box of valueObj.boundingBoxes) {
+            //             const hash = [valueObj.page, ...box].join();
+            //             if (labelHash.has(hash)) {
+            //                 const reason = interpolate(
+            //                     strings.errors.duplicateBoxInLabelFile.message, { page: valueObj.page });
+            //                 toast.error(reason, { autoClose: false });
+            //                 throw new Error("Invalid label file");
+            //             }
+            //             labelHash.add(hash);
+            //         }
+            //     }
+            // }
+            // toast.dismiss();
             return {
                 asset: { ...newAsset, labelingState: labelData.labelingState },
                 regions: [],
@@ -436,13 +440,106 @@ export class AssetService {
      * Delete a tag from asset metadata files
      * @param tagName Name of tag to delete
      */
-    public async deleteTag(tagName: string): Promise<IAssetMetadata[]> {
+    public async deleteTag(tagName: string, tagType: FieldType, tagFormat: FieldFormat): Promise<IAssetMetadata[]> {
         const transformer = (tagNames) => tagNames.filter((t) => t !== tagName);
         const labelTransformer = (labelData: ILabelData) => {
-            labelData.labels = labelData.labels.filter((label) => label.label !== tagName);
+            if (tagType === FieldType.Object || tagType === FieldType.Array) {
+                labelData.labels = labelData.labels.filter((label) => label.label.split("/")[0].replace(/\~1/g, "/").replace(/\~0/g, "~") !== tagName);
+            } else {
+                if (labelData.$schema === constants.labelsSchema) {
+                    labelData.labels = labelData.labels.filter((label) => label.label.replace(/\~1/g, "/").replace(/\~0/g, "~") !== tagName);
+                } else {
+                    labelData.labels = labelData.labels.filter((label) => label.label !== tagName);
+                }
+            }
             return labelData;
         };
-        return await this.getUpdatedAssets(tagName, transformer, labelTransformer);
+        return await this.getUpdatedAssets(tagName, tagType, tagFormat, transformer, labelTransformer);
+    }
+
+    public async refactorTableTag(originalTagName: string, tagName: string, tagType: FieldType, tagFormat: FieldFormat, visualizationHint: TableVisualizationHint, deletedColumns: ITableConfigItem[], deletedRows: ITableConfigItem[], newRows: ITableConfigItem[], newColumns: ITableConfigItem[]): Promise<IAssetMetadata[]> {
+        const transformer = (tagNames, columnKey, rowKey) => {
+            let newTags = tagNames;
+            let newColumnKey = columnKey;
+            let newRowKey = rowKey;
+            if (tagNames[0] === originalTagName) {
+                const hasDeletedRowOrKey = deletedColumns?.find((deletedColumn) => deletedColumn.originalName === columnKey) || deletedRows?.find((deletedRow) => deletedRow.originalName === rowKey);
+                if (hasDeletedRowOrKey) {
+                    newTags = [];
+                    newColumnKey = undefined;
+                    newRowKey = undefined
+                    return { newTags, newColumnKey, newRowKey }
+                }
+                const columnRenamed = newColumns?.find((newColumn) => newColumn.originalName === columnKey && newColumn.originalName !== newColumn.name)
+                const rowRenamed = newRows?.find((newRow) => newRow.originalName === rowKey && newRow.originalName !== newRow.name)
+                if (columnRenamed) {
+                    newColumnKey = columnRenamed.name;
+                }
+                if (rowRenamed) {
+                    newRowKey = rowRenamed.name
+                }
+            }
+            return { newTags, newColumnKey, newRowKey }
+        }
+        const labelTransformer = (labelData: ILabelData) => {
+            labelData.labels = labelData?.labels?.reduce((result, label) => {
+                const labelString = label.label.split("/").map((layer) => { return layer.replace(/\~1/g, "/").replace(/\~0/g, "~") });
+                if (labelString.length > 1) {
+                    const labelTagName = labelString[0];
+                    if (labelTagName !== originalTagName) {
+                        result.push(label);
+                        return result;
+                    }
+                    let columnKey;
+                    let rowKey;
+                    if (tagType === FieldType.Object) {
+                        if (visualizationHint === TableVisualizationHint.Vertical) {
+                            rowKey = labelString[1]
+                            columnKey = labelString[2];
+                        } else {
+                            columnKey = labelString[1]
+                            rowKey = labelString[2];
+                        }
+                        if (deletedRows?.find((deletedRow) => deletedRow.originalName === rowKey) || deletedColumns?.find((deletedColumn) => deletedColumn.originalName === columnKey)) {
+                            return result;
+                        }
+                        const column = newColumns?.find((newColumn) => newColumn.originalName === columnKey)
+                        const row = newRows?.find((newRow) => newRow.originalName === rowKey)
+                        if (visualizationHint === TableVisualizationHint.Vertical) {
+                            result.push({
+                                ...label,
+                                label: tagName.replace(/\~/g, "~0").replace(/\//g, "~1") + "/" + (row?.name || rowKey).replace(/\~/g, "~0").replace(/\//g, "~1") + "/" + (column?.name || columnKey).replace(/\~/g, "~0").replace(/\//g, "~1"),
+                            })
+                        } else {
+                            result.push({
+                                ...label,
+                                label: tagName.replace(/\~/g, "~0").replace(/\//g, "~1") + "/" + (column?.name || columnKey).replace(/\~/g, "~0").replace(/\//g, "~1") + "/" + (row?.name || rowKey).replace(/\~/g, "~0").replace(/\//g, "~1"),
+                            })
+                        }
+
+                    } else {
+                        rowKey = labelString[1]
+                        columnKey = labelString[2];
+                        if (deletedColumns?.find((deletedColumn) => deletedColumn.originalName === columnKey)) {
+                            return result;
+                        }
+                        const column = newColumns?.find((newColumn) => newColumn.originalName === columnKey)
+                        result.push({
+                            ...label,
+                            label: tagName.replace(/\~/g, "~0").replace(/\//g, "~1") + "/" + rowKey.replace(/\~/g, "~0").replace(/\//g, "~1") + "/" + (column?.name || columnKey).replace(/\~/g, "~0").replace(/\//g, "~1"),
+                        })
+                    }
+                    return result;
+
+                } else {
+                    result.push(label);
+                    return result;
+                }
+            }, [])
+            return labelData;
+        }
+
+        return await this.getUpdatedAssetsAfterReconfigure(originalTagName, tagName, tagType, tagFormat, transformer, labelTransformer);
     }
 
     /**
@@ -458,7 +555,7 @@ export class AssetService {
             }
             return labelData;
         };
-        return await this.getUpdatedAssets(tagName, transformer, labelTransformer);
+        return await this.getUpdatedAssets(tagName, null, null, transformer, labelTransformer);
     }
 
     /**
@@ -468,14 +565,34 @@ export class AssetService {
      */
     private async getUpdatedAssets(
         tagName: string,
+        tagType: FieldType,
+        tagFormat: FieldFormat,
         transformer: (tags: string[]) => string[],
         labelTransformer: (label: ILabelData) => ILabelData)
         : Promise<IAssetMetadata[]> {
         // Loop over assets and update if necessary
         const updates = await _.values(this.project.assets).mapAsync(async (asset) => {
             const assetMetadata = await this.getAssetMetadata(asset);
-            const isUpdated = this.updateTagInAssetMetadata(assetMetadata, tagName, transformer, labelTransformer);
+            const isUpdated = this.updateTagInAssetMetadata(assetMetadata, tagName, tagType, tagFormat, transformer, labelTransformer);
+
+            return isUpdated ? assetMetadata : null;
+        });
 
+        return updates.filter((assetMetadata) => !!assetMetadata);
+    }
+
+    private async getUpdatedAssetsAfterReconfigure(
+        originalTagName: string,
+        tagName: string,
+        tagType: FieldType,
+        tagFormat: FieldFormat,
+        transformer: (tags: string[], columnKey: string, rowKey: string) => any,
+        labelTransformer: (label: ILabelData) => ILabelData)
+        : Promise<IAssetMetadata[]> {
+        // Loop over assets and update if necessary
+        const updates = await _.values(this.project.assets).mapAsync(async (asset) => {
+            const assetMetadata = await this.getAssetMetadata(asset);
+            const isUpdated = this.reconfigureTableTagInAssetMetadata(assetMetadata, originalTagName, tagName, tagType, tagFormat, transformer, labelTransformer);
             return isUpdated ? assetMetadata : null;
         });
 
@@ -492,6 +609,8 @@ export class AssetService {
     private updateTagInAssetMetadata(
         assetMetadata: IAssetMetadata,
         tagName: string,
+        tagType: FieldType,
+        tagFormat: FieldFormat,
         transformer: (tags: string[]) => string[],
         labelTransformer: (labelData: ILabelData) => ILabelData): boolean {
         let foundTag = false;
@@ -502,36 +621,82 @@ export class AssetService {
                 region.tags = transformer(region.tags);
             }
         }
+        if (tagType === FieldType.Array || tagType === FieldType.Object) {
+            if (assetMetadata?.labelData?.labels) {
+                const field = assetMetadata.labelData.labels.find((field) => field.label.split("/")[0].replace(/\~1/g, "/").replace(/\~0/g, "~") === tagName);
+                if (field) {
+                    foundTag = true;
+                    assetMetadata.labelData = labelTransformer(assetMetadata.labelData);
+                }
+            }
+        } else {
+            if (assetMetadata.labelData && assetMetadata.labelData.labels) {
+                const field = assetMetadata.labelData.labels.find((field) => field.label === tagName);
+                if (field) {
+                    foundTag = true;
+                }
+            }
+        }
+
+        if (foundTag) {
+            assetMetadata.labelData = labelTransformer(assetMetadata.labelData);
+            assetMetadata.regions = assetMetadata.regions.filter((region) => region.tags.length > 0);
+            assetMetadata.asset.state = _.get(assetMetadata, "labelData.labels.length") || _.get(assetMetadata, "labelData.tableLabels.length")
+                ? AssetState.Tagged : AssetState.Visited;
+            return true;
+        }
+
+        return false;
+    }
 
-        if (assetMetadata.labelData && assetMetadata.labelData.labels) {
-            const field = assetMetadata.labelData.labels.find((field) => field.label === tagName);
+    private reconfigureTableTagInAssetMetadata(
+        assetMetadata: IAssetMetadata,
+        originalTagName: string,
+        tagName: string,
+        tagType: FieldType,
+        tagFormat: FieldFormat,
+        transformer: any,
+        labelTransformer: (labelData: ILabelData) => ILabelData): boolean {
+        let foundTag = false;
+        for (const region of assetMetadata.regions) {
+            if (region.tags.find((t) => t === originalTagName)) {
+                foundTag = true;
+                const { newTags, newColumnKey, newRowKey } = transformer((region as ITableRegion).tags, (region as ITableRegion).columnKey, (region as ITableRegion).rowKey);
+                region.tags = newTags;
+                (region as ITableRegion).columnKey = newColumnKey;
+                (region as ITableRegion).rowKey = newRowKey;
+
+            }
+        }
+        if (tagType === FieldType.Array || tagType === FieldType.Object) {
+            const field = assetMetadata?.labelData?.labels?.find((field) => field.label.split("/")[0] === originalTagName.replace(/\~/g, "~0").replace(/\//g, "~1"));
             if (field) {
                 foundTag = true;
-                assetMetadata.labelData = labelTransformer(assetMetadata.labelData);
-                if(assetMetadata.labelData.labels.length===0){
-                    delete assetMetadata.labelData.labelingState;
-                    delete assetMetadata.asset.labelingState;
-                }
             }
         }
+
         if (foundTag) {
+            assetMetadata.labelData = labelTransformer(assetMetadata.labelData);
+            if (assetMetadata.labelData.labels.length === 0) {
+                delete assetMetadata.labelData.labelingState;
+                delete assetMetadata.asset.labelingState;
+            }
             assetMetadata.regions = assetMetadata.regions.filter((region) => region.tags.length > 0);
-            assetMetadata.asset.state = _.get(assetMetadata, "labelData.labels.length")
+            assetMetadata.asset.state = _.get(assetMetadata, "labelData.labels.length") || _.get(assetMetadata, "labelData.tableLabels.length")
                 ? AssetState.Tagged : AssetState.Visited;
-            if(assetMetadata.asset.labelingState===AssetLabelingState.Trained){
-                assetMetadata.asset.labelingState=AssetLabelingState.ManuallyLabeled;
-                if(assetMetadata.labelData){
-                    assetMetadata.labelData.labelingState=AssetLabelingState.ManuallyLabeled;
+            if (assetMetadata.asset.labelingState === AssetLabelingState.Trained) {
+                assetMetadata.asset.labelingState = AssetLabelingState.ManuallyLabeled;
+                if (assetMetadata.labelData) {
+                    assetMetadata.labelData.labelingState = AssetLabelingState.ManuallyLabeled;
                 }
-            }else if(assetMetadata.asset.labelingState===AssetLabelingState.AutoLabeled){
-                assetMetadata.asset.labelingState=AssetLabelingState.AutoLabeledAndAdjusted;
-                if(assetMetadata.labelData){
-                    assetMetadata.labelData.labelingState=AssetLabelingState.AutoLabeledAndAdjusted;
+            } else if (assetMetadata.asset.labelingState === AssetLabelingState.AutoLabeled) {
+                assetMetadata.asset.labelingState = AssetLabelingState.AutoLabeledAndAdjusted;
+                if (assetMetadata.labelData) {
+                    assetMetadata.labelData.labelingState = AssetLabelingState.AutoLabeledAndAdjusted;
                 }
             }
             return true;
         }
-
         return false;
     }
 
diff --git a/src/services/projectService.ts b/src/services/projectService.ts
index 63ea46303..0d2b63a98 100644
--- a/src/services/projectService.ts
+++ b/src/services/projectService.ts
@@ -10,6 +10,7 @@ import {
     FieldFormat,
     IField,
     IFieldInfo,
+    ITableTag, ITableField, TableHeaderTypeAndFormat, ITableLabel, ILabelData, TableVisualizationHint
 } from "../models/applicationState";
 import Guard from "../common/guard";
 import { constants } from "../common/constants";
@@ -17,6 +18,7 @@ import { decryptProject, encryptProject, joinPath, patch, getNextColor } from ".
 import packageJson from "../../package.json";
 import { strings, interpolate } from "../common/strings";
 import { toast } from "react-toastify";
+import clone from "rfdc";
 
 // tslint:disable-next-line:no-var-requires
 const tagColors = require("../react/components/common/tagColors.json");
@@ -40,7 +42,7 @@ export interface IProjectService {
     delete(project: IProject): Promise<void>;
     isDuplicate(project: IProject, projectList: IProject[]): boolean;
     updateProjectTagsFromFiles(oldProject: IProject): Promise<IProject>;
-    updatedAssetMetadata(oldProject: IProject, assetDocumentCountDifference: []): Promise<IProject>;
+    updatedAssetMetadata(oldProject: IProject, assetDocumentCountDifference: any, columnDocumentCountDifference: any, rowDocumentCountDifference: any): Promise<IProject>;
 }
 
 /**
@@ -192,23 +194,27 @@ export default class ProjectService implements IProjectService {
         }
     }
 
-    public async updatedAssetMetadata(project: IProject,  assetDocumentCountDifference: any): Promise<IProject> {
-        const updatedProject = Object.assign({}, project);
-        const tags: ITag[] = [];
-        updatedProject.tags.forEach((tag) => {
-            const diff = assetDocumentCountDifference[tag.name];
+    public async updatedAssetMetadata(project: IProject,  assetDocumentCountDifference: any, columnDocumentCountDifference?: any,
+        rowDocumentCountDifference?: any): Promise<IProject> {
+        const updatedProject = clone()(project);
+        updatedProject.tags?.forEach((tag: ITag) => {
+            const diff = assetDocumentCountDifference?.[tag.name];
             if (diff) {
-                tags.push({
-                    ...tag,
-                    documentCount: tag.documentCount + diff,
-                } as ITag);
-            } else {
-                tags.push({
-                    ...tag,
-                } as ITag);
+                tag.documentCount += diff;
+            }
+            if (tag.type === FieldType.Object || tag.type === FieldType.Array) {
+                // (tag as ITableTag).columnKeys?.forEach((columnKey) => {
+                //     if (columnDocumentCountDifference?.[tag.name]?.[columnKey.fieldKey]) {
+                //         columnKey.documentCount += columnDocumentCountDifference[tag.name][columnKey.fieldKey];
+                //     }
+                // });
+                // (tag as ITableTag).rowKeys?.forEach((rowKey) => {
+                //     if (rowDocumentCountDifference?.[tag.name]?.[rowKey.fieldKey]) {
+                //         rowKey.documentCount += rowDocumentCountDifference[tag.name][rowKey.fieldKey]
+                //     }
+                // });
             }
         });
-        updatedProject.tags = tags;
         if (JSON.stringify(updatedProject.tags) === JSON.stringify(project.tags)) {
             return project;
         } else {
@@ -241,13 +247,22 @@ export default class ProjectService implements IProjectService {
                     && blobs.has(blob.substr(0, blob.length - constants.labelFileExtension.length))) {
                     try {
                         if (!assetLabel || assetLabel === blob) {
-                            const content = JSON.parse(await storageProvider.readText(blob));
+                            const content = JSON.parse(await storageProvider.readText(blob)) as ILabelData;
                             content.labels.forEach((label) => {
-                                tagNameSet.add(label.label);
-                                if (tagDocumentCount[label.label]) {
-                                    tagDocumentCount[label.label] += 1;
+                                if (content.$schema === constants.labelsSchema && label.label.split("/").length > 1) {
+                                    return;
+                                }
+                                let labelName;
+                                if (content.$schema === constants.labelsSchema) {
+                                    labelName = label.label.replace(/\~1/g, "/").replace(/\~0/g, "~");
+                                } else {
+                                    labelName = label.label
+                                }
+                                tagNameSet.add(labelName);
+                                if (tagDocumentCount[labelName]) {
+                                    tagDocumentCount[labelName] += 1;
                                 } else {
-                                    tagDocumentCount[label.label] = 1;
+                                    tagDocumentCount[labelName] = 1;
                                 }
                             });
                         }
@@ -302,13 +317,47 @@ export default class ProjectService implements IProjectService {
             const fieldInfo = JSON.parse(json) as IFieldInfo;
             const tags: ITag[] = [];
             fieldInfo.fields.forEach((field, index) => {
-                tags.push({
-                    name: field.fieldKey,
-                    color: tagColors[index],
-                    type: normalizeFieldType(field.fieldType),
-                    format: field.fieldFormat,
-                    documentCount: 0,
-                } as ITag);
+                if (field.fieldType === FieldType.Object || field.fieldType === FieldType.Array) {
+                    const tableDefinition = fieldInfo?.definitions?.[field.fieldKey + "_object"];
+                    if (!tableDefinition) {
+                        toast.info("Table field " + field.fieldKey + " has no definition.")
+                        return;
+                    }
+                    if (field.fieldType === FieldType.Object) {
+                        tags.push({
+                            name: field.fieldKey,
+                            color: tagColors[index],
+                            type: normalizeFieldType(field.fieldType),
+                            format: field.fieldFormat,
+                            documentCount: 0,
+                            itemType: (field as ITableField).itemType,
+                            fields: (field as ITableField).fields,
+                            definition: tableDefinition,
+                            visualizationHint: (field as ITableField).visualizationHint || TableVisualizationHint.Vertical
+                        } as ITableTag);
+                    } else {
+                        tags.push({
+                            name: field.fieldKey,
+                            color: tagColors[index],
+                            type: normalizeFieldType(field.fieldType),
+                            format: field.fieldFormat,
+                            documentCount: 0,
+                            itemType: (field as ITableField).itemType,
+                            fields: (field as ITableField).fields,
+                            definition: tableDefinition,
+                            visualizationHint: null,
+                        } as ITableTag);
+                    }
+
+                } else {
+                    tags.push({
+                        name: field.fieldKey,
+                        color: tagColors[index],
+                        type: normalizeFieldType(field.fieldType),
+                        format: field.fieldFormat,
+                        documentCount: 0,
+                    } as ITag);
+                }
             });
             if (project.tags) {
                 project.tags = patch(project.tags, tags, "name", ["type", "format"]);
@@ -372,13 +421,33 @@ export default class ProjectService implements IProjectService {
         Guard.null(project);
         Guard.null(project.tags);
 
-        const fieldInfo = {
-            fields: project.tags.map((tag) => ({
-                fieldKey: tag.name,
-                fieldType: tag.type ? tag.type : FieldType.String,
-                fieldFormat: tag.format ? tag.format : FieldFormat.NotSpecified,
-            } as IField)),
-        };
+        const definitions = {};
+        const fieldInfo = {};
+        fieldInfo["$schema"] = "http://www.azure.com/schema/formrecognizer/fields.json"
+        fieldInfo["fields"] =
+            project.tags.map((tag ) => {
+                if (tag.type === FieldType.Object || tag.type === FieldType.Array) {
+                    const tableField = {
+                        fieldKey: tag.name,
+                        fieldType: tag.type ? tag.type : FieldType.String,
+                        fieldFormat: tag.format ? tag.format : FieldFormat.NotSpecified,
+                        itemType: (tag as ITableTag).itemType,
+                        fields: (tag as ITableTag).fields,
+                    } as ITableField;
+                    if (tag.type === FieldType.Object) {
+                        tableField.visualizationHint = (tag as ITableTag).visualizationHint
+                    }
+                    definitions[(tag as ITableTag).definition.fieldKey] = (tag as ITableTag).definition;
+                    return tableField;
+                } else {
+                    return ({
+                        fieldKey: tag.name,
+                        fieldType: tag.type ? tag.type : FieldType.String,
+                        fieldFormat: tag.format ? tag.format : FieldFormat.NotSpecified,
+                    } as IField)
+                }
+            })
+        fieldInfo["definitions"] = definitions;
 
         const fieldFilePath = joinPath("/", project.folderPath, constants.fieldsFileName);
         await storageProvider.writeText(fieldFilePath, JSON.stringify(fieldInfo, null, 4));
diff --git a/yarn.lock b/yarn.lock
index 832750438..1d94cf28b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8472,7 +8472,7 @@ mime@1.6.0:
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
   integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
 
-mime@^2.4.4, mime@^2.4.6:
+mime@^2.4.4, mime@^2.4.5, mime@^2.4.6:
   version "2.4.6"
   resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
   integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
@@ -11508,6 +11508,11 @@ rework@1.0.1:
     convert-source-map "^0.3.3"
     css "^2.0.0"
 
+rfdc@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2"
+  integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==
+
 rgb-regex@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
@@ -11794,6 +11799,13 @@ serialize-javascript@^3.1.0:
   dependencies:
     randombytes "^2.1.0"
 
+serialize-javascript@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
+  integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
+  dependencies:
+    randombytes "^2.1.0"
+
 serve-index@^1.9.1:
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"