You can do this with a series of depth-first searches, starting from each cell.
Your starting cell's letter forms the initial word prefix. In each iteration, you check if some word in your dictionary either wholly matches the current prefix (if so, add the word and the indexes you traversed to get here to the possible answer set) or if no word in your dictionary starts with this prefix (if so, abort this search branch, backtracking to the next shorter prefix).
Then you examine the neighbours of the current cell, and if any have not already been crossed in the path you took to get here, recurse on them, adding their index to your path-so-far and their letter to the end of your prefix.
If you sort your dictionary in lexicographical order, then all words matching a particular prefix will correspond to a contiguous range of indices in that list, which you can represent with a start/end index or start and count. Then you only ever have to search smaller and smaller ranges of your list, not the whole thing.
Or if you want to get fancy, you could implement your dictionary as a Finite State Acceptor that accepts only valid words, or a Transducer that maps valid words to their corresponding string or id, or a trie similarly. Then your search is effectively a constrained walk of the state graph of this data structure.